Menu

Change language

GitHub repo

FAQ — Two Column

blockmarketing

Frequently asked questions split across two columns of independent accordions, so a long answer in one column never pushes the questions beside it out of alignment.

Installation

$npx shadcn@latest add https://uipkge.dev/r/lit/faq-two-column.json
Manifest:https://uipkge.dev/r/lit/faq-two-column.jsonInstalls to:src/uip/blocks/faq-two-column/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

HTMLElementTagNameMap
interface HTMLElementTagNameMap {
    'uip-faq-two-column': UipFaqTwoColumn
  }
}

Files installed (1)

  • src/uip/blocks/faq-two-column/faq-two-column.ts4.2 kB
    import { LitElement, css, html } from 'lit'
    import { ArrowRight } from 'lucide'
    import { tailwind } from '../../lib/styles'
    import { ThemeController } from '../../lib/theme'
    import { icon } from '../../lib/icon'
    // Side-effect imports: register the composed elements for standalone use
    // (consumers who install only this block still get working primitives).
    import '../../components/accordion/accordion'
    import '../../components/badge/badge'
    import '../../components/button/button'
    
    const faqs = [
      {
        q: 'Does our data leave the warehouse?',
        a: 'No. Metrics resolve against your warehouse at query time using a read-only role. We store definitions and query metadata, never rows.',
      },
      {
        q: 'How long does a rollout take?',
        a: 'Five weeks from kickoff to hand-over is typical: a week to connect, a week to model, two to pilot with one team, one to roll out.',
      },
      {
        q: 'What happens when a definition changes?',
        a: 'It opens a pull request. Once merged, every consumer resolves the new definition at once, and reverting is the same single click.',
      },
      {
        q: 'Can we keep using our BI tool?',
        a: 'Yes. Looker, Hex, and Sigma read certified metrics through the API, so they stop maintaining their own competing definitions.',
      },
      {
        q: 'How is access controlled?',
        a: 'Row-level scope resolves per query from your identity provider. A shared dashboard link cannot show a viewer more than their own scope.',
      },
      {
        q: 'What does it cost to run?',
        a: 'Query budgets are enforced per team at run time, so warehouse spend is capped rather than reconciled from an invoice a month later.',
      },
    ]
    
    // Deal alternately so the two columns stay balanced regardless of answer length.
    const columns = [faqs.filter((_, index) => index % 2 === 0), faqs.filter((_, index) => index % 2 === 1)]
    
    /**
     * <uip-faq-two-column> — the registry FaqTwoColumn block as a web component.
     *
     * Class strings are React's FaqTwoColumn verbatim. The two independent
     * accordions are <uip-accordion> (type multiple) with flat light-DOM
     * trigger/content children paired by `data-value`, so a long answer on the
     * left never shifts the questions on the right.
     */
    export class UipFaqTwoColumn extends LitElement {
      static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }
      // :host display can't be a utility class (the host has no template of its own).
      static styles = [tailwind, css`:host { display: block; }`]
    
      constructor() {
        super()
        new ThemeController(this)
      }
    
      connectedCallback() {
        super.connectedCallback()
        this.setAttribute('data-uipkge', '')
        this.setAttribute('data-slot', 'faq-two-column')
      }
    
      render() {
        return html`
          <section data-slot="faq-two-column" class="bg-background">
            <div class="mx-auto max-w-6xl px-6 py-20 lg:py-28">
              <div class="max-w-2xl">
                <uip-badge variant="secondary">FAQ</uip-badge>
                <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">Questions we get on the first call</h2>
              </div>
    
              <div class="mt-10 grid gap-x-12 gap-y-0 md:grid-cols-2">
                ${columns.map(
                  (column) => html`<uip-accordion type="multiple" class="w-full">
                    ${column.map(
                      (faq) => html`<span slot="trigger" data-value=${faq.q} class="text-left text-sm font-medium"
                          >${faq.q}</span
                        >
                        <div slot="content" data-value=${faq.q} class="text-muted-foreground text-sm leading-relaxed">
                          ${faq.a}
                        </div>`,
                    )}
                  </uip-accordion>`,
                )}
              </div>
    
              <div class="border-border mt-10 flex flex-wrap items-center justify-between gap-4 border-t pt-6">
                <p class="text-muted-foreground text-sm">Something not covered here?</p>
                <uip-button variant="outline">
                  Talk to an engineer ${icon(ArrowRight, 'arrow-right', 'ml-2 size-4')}
                </uip-button>
              </div>
            </div>
          </section>
        `
      }
    }
    
    customElements.get('uip-faq-two-column') || customElements.define('uip-faq-two-column', UipFaqTwoColumn)
    
    declare global {
      interface HTMLElementTagNameMap {
        'uip-faq-two-column': UipFaqTwoColumn
      }
    }
    

Raw manifest:https://uipkge.dev/r/lit/faq-two-column.json