voorhoede / head-start

Base setup on top of headless services to help you quickly start a new website
ISC License
3 stars 0 forks source link

Tabs Component #96

Closed jbmoelker closed 8 months ago

jbmoelker commented 9 months ago

User story

As a website visitor, I want content grouped in tabs or accordions, so that I can more quickly scan, understand and find specific content in large content.

System design

There are many implementations available for a Tabs Component, so there's little need to invent our own. For Head Start we're looking for unstyled components, so something like Headless UI's Tabs Component would be an option. But for Head Start part of our philosophy is also to leave selecting a specific JS framework for the UI to the project developers. So maybe our best option is to create our Tabs Component based on the Howto Tabs Web Component on web.dev. It progressively enhances a set of content regions into a tabbed interface, with focus on accessibility and performance. And its still unstyled.

We already have a Page Partial Block in Head Start which we could slightly extend to supports Tabs. The Partial Block currently has a modular items field where each item has a title and a blocks field. Right now the item title is only used internally in the CMS. We could use this title to be the tab title, the blocks would be the tab panel and the whole component would be the tab group. We could add a layout field to the Page Partial Block which could still default to stack and extend it with a tabs option:

Page Partial Block
  - items
     - title
     - blocks
+ - layout ('stack' | 'tabs')

The Page Partial Block template could then be something like this:

---
import type { PagePartialBlockFragment } from '@lib/types/datocms';
import type { AnyBlock } from '@blocks/Blocks';
import Blocks from '@blocks/Blocks.astro';
+ import Tabs from '@components/Tabs';

interface Props {
  block: PagePartialBlockFragment
}
const { block } = Astro.props;
const items = block.items as { blocks: AnyBlock[] }[];
---

+ { block.layout === 'stack' && (
    items.map(item => (
      <Blocks blocks={item.blocks} />
   ))
+ )}

+ { block.layout === 'tabs' && (
+   <Tabs.Group>
+      { items.map(item => (
+       <Tabs.Tab>{ item.title }</Tabs.Tab>
+       <Tabs.Panel>
+         <Blocks blocks={item.blocks} />
+       </Tabs.Panel>
+      )) }
+   </Tabs.Group>
+ )}
jbmoelker commented 9 months ago

Figured this trick would work not just for tabs, but also for accordions. Made a PR: #98 .