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 uipkge-ng@latest add faq-two-column
Manifest:https://uipkge.dev/r/angular/faq-two-column.jsonInstalls to:src/app/components/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.

FaqEntry
interface FaqEntry {
  q: string
  a: string
}

npm dependencies

Files installed (2)

  • src/app/components/blocks/faq-two-column/faq-two-column.component.ts4.4 kB
    // Frequently asked questions split across two independent accordion columns, so a
    // long answer on the left never pushes the questions on the right out of alignment.
    // Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'
    import { ArrowRight, LucideAngularModule } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import {
      UiAccordionComponent,
      UiAccordionContentComponent,
      UiAccordionItemComponent,
      UiAccordionTriggerComponent,
    } from '@/ui/accordion/accordion.component'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiButtonComponent } from '@/ui/button/button.component'
    
    export interface FaqEntry {
      q: string
      a: string
    }
    
    const faqs: FaqEntry[] = [
      {
        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.',
      },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-faq-two-column, [ui-faq-two-column]',
      standalone: true,
      // React renders the root <section> itself: the host stays out of layout and `class` goes to the root.
      host: { '[attr.class]': '"contents"' },
      imports: [
        LucideAngularModule,
        UiAccordionComponent,
        UiAccordionContentComponent,
        UiAccordionItemComponent,
        UiAccordionTriggerComponent,
        UiBadgeComponent,
        UiButtonComponent,
      ],
      template: `
        <section data-slot="faq-two-column" [class]="rootClass">
          <div class="mx-auto max-w-6xl px-6 py-20 lg:py-28">
            <div class="max-w-2xl">
              <span ui-badge variant="secondary">FAQ</span>
              <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">Questions we get on the first call</h2>
            </div>
    
            <!-- Two independent accordions: a long answer on the left cannot push the
                 questions on the right out of alignment. -->
            <div class="mt-10 grid gap-x-12 gap-y-0 md:grid-cols-2">
              @for (column of columns(); track $index) {
                <ui-accordion type="multiple" class="w-full">
                  @for (faq of column; track faq.q) {
                    <ui-accordion-item [value]="faq.q">
                      <button ui-accordion-trigger class="text-left text-sm font-medium">{{ faq.q }}</button>
                      <ui-accordion-content class="text-muted-foreground text-sm leading-relaxed">
                        {{ faq.a }}
                      </ui-accordion-content>
                    </ui-accordion-item>
                  }
                </ui-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>
              <button ui-button variant="outline">
                Talk to an engineer
                <lucide-icon [img]="ArrowRight" class="ml-2 size-4" aria-hidden="true" />
              </button>
            </div>
          </div>
        </section>
      `,
    })
    export class UiFaqTwoColumnComponent {
      protected readonly ArrowRight = ArrowRight
    
      protected readonly faqs = faqs
    
      @Input('class') className?: string
    
      // Deal alternately so the two columns stay balanced regardless of answer length.
      readonly columns = computed<FaqEntry[][]>(() => [
        faqs.filter((_, index) => index % 2 === 0),
        faqs.filter((_, index) => index % 2 === 1),
      ])
    
      get rootClass(): string {
        return cn('bg-background', this.className)
      }
    }
    
  • src/app/components/blocks/faq-two-column/index.ts0.1 kB

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