@staffbase/design-mcp 19.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,226 @@
1
+ [
2
+ {
3
+ "slug": "components/alert-dialog",
4
+ "title": "Alert Dialog",
5
+ "description": "Alert dialogs interrupt the user to confirm a critical or destructive action. Use for actions that cannot be undone.",
6
+ "group": "Components",
7
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n <ComponentPreview>\n <AlertDialogBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {AlertDialog, BinIcon, Button} from '@staffbase/design';\n\n<AlertDialog.Root>\n <AlertDialog.Trigger>\n <Button color=\"critical\">Delete item</Button>\n </AlertDialog.Trigger>\n <AlertDialog.Popup>\n <AlertDialog.Icon>\n <BinIcon />\n </AlertDialog.Icon>\n <AlertDialog.Title>Delete this item?</AlertDialog.Title>\n <AlertDialog.Description>\n This action cannot be undone. The item will be permanently removed.\n </AlertDialog.Description>\n <AlertDialog.Action color=\"critical\">Delete</AlertDialog.Action>\n <AlertDialog.Cancel>Cancel</AlertDialog.Cancel>\n </AlertDialog.Popup>\n</AlertDialog.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {AlertDialog} from '@staffbase/design';\n\n<AlertDialog.Root>\n <AlertDialog.Trigger />\n <AlertDialog.Popup>\n <AlertDialog.Icon />\n <AlertDialog.Title />\n <AlertDialog.Description />\n <AlertDialog.Action />\n <AlertDialog.Cancel />\n </AlertDialog.Popup>\n</AlertDialog.Root>\n```\n</Snippet>\n\n## API reference\n\n### Root\n\n<BuiltOn url=\"https://base-ui.com/react/components/alert-dialog#root\">Base UI Alert Dialog Root</BuiltOn>\n\n### Trigger\n\n<BuiltOn url=\"https://base-ui.com/react/components/alert-dialog#trigger\">Base UI Alert Dialog Trigger</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'Opens the dialog when activated. Must be a button element.',\n },\n ]}\n/>\n\n### Popup\n\n<BuiltOn url=\"https://base-ui.com/react/components/alert-dialog#popup\">Base UI Alert Dialog Popup</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'container',\n type: ['HTMLElement', 'RefObject<HTMLElement>'],\n description:\n 'Use this instead of z-index to specify another parent element to render the dialog portal into. Default is the document body.',\n },\n ]}\n/>\n\n### Icon\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'A wrapper to style the icon to render in the dialog.',\n },\n ]}\n/>\n\n### Title\n\nRenders an `<h2>` element.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'A heading that labels the dialog.',\n },\n ]}\n/>\n\n### Description\n\nRenders a `<p>` element.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'Additional information about the action requested by the dialog.',\n },\n ]}\n/>\n </Fragment>\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n Alert dialog anatomy shows the structure and placement of key elements: icon, title, description, and action buttons. Understanding these elements helps maintain consistent and accessible dialogs.\n\n <div class=\"grid gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Alert dialog anatomy full example\" src=\"/components/alert-dialog/guidelines/figma-01.png\" class=\"md:col-span-2\" />\n <img alt=\"Alert dialog anatomy example 1\" src=\"/components/alert-dialog/guidelines/figma-02.png\" />\n <img alt=\"Alert dialog anatomy example 2\" src=\"/components/alert-dialog/guidelines/figma-03.png\" />\n </div>\n\n ## Properties\n\n ### Icon\n\n An optional icon can be displayed at the top of an alert dialog to reinforce the action type or urgency level. Icons should be simple and universally recognized.\n\n <div class=\"not-prose\"><img alt=\"Alert dialog with icon example\" src=\"/components/alert-dialog/guidelines/figma-04.png\" /></div>\n\n ### Button Types\n\n Buttons within an alert dialog can be set to any button type. Use critical buttons for destructive actions and secondary buttons for cancel actions to provide clear visual hierarchy.\n\n <div class=\"not-prose\"><img alt=\"Alert dialog button types example\" src=\"/components/alert-dialog/guidelines/figma-05.png\" /></div>\n\n ## Behaviour\n\n ### Overlay\n\n Alert dialogs are always placed above an overlay (or backdrop) that uses the overlay color. This ensures the dialog remains the focus of user interaction.\n\n <div class=\"not-prose\"><img alt=\"Alert dialog overlay example\" src=\"/components/alert-dialog/guidelines/figma-06.png\" /></div>\n\n ## Best Practices\n\n ### When to use a dialog?\n\n Most often used when a user has taken an action that has big consequences. Alert dialogs confirm that the user really wants to take the destructive or major action.\n\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <div>\n <img alt=\"Major action example\" src=\"/components/alert-dialog/guidelines/figma-07.png\" />\n <p class=\"mt-3 text-body-sm\">Major action like sending email to many recipients. Title can be a question. Button color: blue.</p>\n </div>\n <div>\n <img alt=\"Destructive action example\" src=\"/components/alert-dialog/guidelines/figma-08.png\" />\n <p class=\"mt-3 text-body-sm\">Destructive action like deleting or removing items. Phrase title as a question. Button color: red.</p>\n </div>\n </div>\n\n ## Content Guidelines\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Title\n\n Use sentence case and phrase as a question with a question mark. Avoid exclamation points. Use verb+noun format: \"Delete space?\" Don't use \"Cancel\" as the verb of the action.\n\n </div>\n <div class=\"not-prose\"><img alt=\"Alert dialog title guidelines\" src=\"/components/alert-dialog/guidelines/figma-09.png\" /></div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Body Text\n\n Use sentence case and end with a period. Provide a short explanation of the consequences. A second sentence can explain how to reverse the action. Avoid exclamation points.\n\n </div>\n <div class=\"not-prose\"><img alt=\"Alert dialog body text guidelines\" src=\"/components/alert-dialog/guidelines/figma-10.png\" /></div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Buttons\n\n Follow button labeling rules using Title Case. Primary/Critical buttons repeat the verb from the title. Secondary buttons use \"Cancel\".\n\n </div>\n <div class=\"not-prose\"><img alt=\"Alert dialog button guidelines\" src=\"/components/alert-dialog/guidelines/figma-11.png\" /></div>\n </div>\n </div>\n\n ## Usage Examples\n\n <div class=\"not-prose\"><img alt=\"Alert dialog usage example\" src=\"/components/alert-dialog/guidelines/figma-12.png\" /></div>\n\n </Fragment>\n</DocsTabs>"
8
+ },
9
+ {
10
+ "slug": "components/banner",
11
+ "title": "Banner",
12
+ "description": "Banners communicate prominent messages, such as system status, errors, or important information.",
13
+ "group": "Components",
14
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n <ComponentPreview>\n <Banner>Info banner<Banner.CloseButton aria-label=\"Close message\" /></Banner>\n <Fragment slot=\"code\">\n```tsx\nimport {Banner} from '@staffbase/design';\n\n<Banner>\n Info banner\n <Banner.CloseButton aria-label=\"Close message\" />\n</Banner>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Banner} from '@staffbase/design';\n\n<Banner>\n <Banner.CloseButton />\n</Banner>\n```\n</Snippet>\n\n## Examples\n\n### Variants\n\n<ComponentPreview>\n <div className='space-y-2 w-full'>\n <Banner>Info banner (default)</Banner>\n <Banner variant=\"success\">Success banner</Banner>\n <Banner variant=\"warning\">Warning banner</Banner>\n <Banner variant=\"critical\">Critical banner</Banner>\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {Banner} from '@staffbase/design';\n\n<Banner>Info banner (default)</Banner>\n<Banner variant=\"success\">Success banner</Banner>\n<Banner variant=\"warning\">Warning banner</Banner>\n<Banner variant=\"critical\">Critical banner</Banner>\n```\n </Fragment>\n</ComponentPreview>\n\n### Sizes\n\n<ComponentPreview>\n <div className='space-y-2 w-full'>\n <Banner size=\"sm\">Small banner (default)</Banner>\n <Banner size=\"md\">Medium banner</Banner>\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {Banner} from '@staffbase/design';\n\n<Banner size=\"sm\">Small banner (default)</Banner>\n<Banner size=\"md\">Medium banner</Banner>\n```\n </Fragment>\n</ComponentPreview>\n\n### Layouts\n\n<ComponentPreview>\n <div className='space-y-2 w-full'>\n <Banner>Standalone banner (default)</Banner>\n <Banner layout=\"bleed\">Full bleed banner</Banner>\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {Banner} from '@staffbase/design';\n\n<Banner>Standalone banner (default)</Banner>\n<Banner layout=\"bleed\">Full bleed banner</Banner>\n```\n </Fragment>\n</ComponentPreview>\n\n## API reference\n\n### Banner\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n description: 'The content of the Banner.',\n required: true,\n },\n {\n name: 'variant',\n type: ['info', 'success', 'warning', 'critical'],\n description: 'Defines the semantics / use case of Banner.',\n default: 'info',\n },\n {\n name: 'size',\n type: ['sm', 'md'],\n description: 'Changes the size of the Banner.',\n default: 'sm',\n },\n {\n name: 'layout',\n type: ['standalone', 'bleed'],\n description: 'Changes the style of the banner.',\n default: 'standalone',\n },\n ]}\n/>\n\n### Close Button\n\n<PropTable\n rows={[\n {\n name: 'aria-label',\n type: 'string',\n description: 'Accessible label describing the close action for screen readers.',\n required: true,\n },\n ]}\n/>\n\n## Accessibility\n\n### As notification\n\nIf you're using the banner as a notification to tell the user about the outcome of something they've just done, add `role=\"alert\"` to the component so focus shifts to the notification banner.\n\n<ComponentPreview>\n <div className='w-full'>\n <Banner role=\"alert\" variant=\"critical\">\n <div>\n <h2 className=\"me-2 inline font-semibold\">Missing required field</h2>\n <p className=\"inline\">Enter a valid email address.</p>\n </div>\n <Banner.CloseButton aria-label=\"Close message\" />\n </Banner>\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {Banner} from '@staffbase/design';\n\n<Banner role=\"alert\" variant=\"critical\">\n <div>\n <h2 className=\"me-2 inline font-semibold\">Missing required field</h2>\n <p className=\"inline\">Enter a valid email address.</p>\n </div>\n <Banner.CloseButton aria-label=\"Close message\" />\n</Banner>\n```\n </Fragment>\n</ComponentPreview>\n\n### Semantic HTML\n\nIf you're putting multiple elements inside the banner, use semantic HTML β€” for example `h2` for the heading, `p` for the content, and `button` or `a` for the CTA.\n\n </Fragment>\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n Banners have a consistent structure with a background color indicating their variant, optional icon, text content, and optional close button.\n\n <div class=\"flex flex-col gap-8 not-prose\">\n <img alt=\"Banner anatomy example\" src=\"/components/banner/guidelines/figma-01.png\" />\n <img alt=\"Banner anatomy structure row 1\" src=\"/components/banner/guidelines/figma-02.png\" />\n <img alt=\"Banner anatomy structure row 2\" src=\"/components/banner/guidelines/figma-03.png\" />\n </div>\n\n ## Properties\n\n ### Variants\n\n Banners come in four semantic variants to communicate different types of messages: Info for general information, Success for positive outcomes, Warning for cautions, and Critical for errors.\n\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Banner Info variant\" src=\"/components/banner/guidelines/figma-04.png\" />\n <img alt=\"Banner Warning variant\" src=\"/components/banner/guidelines/figma-05.png\" />\n <img alt=\"Banner Success variant\" src=\"/components/banner/guidelines/figma-06.png\" />\n <img alt=\"Banner Critical variant\" src=\"/components/banner/guidelines/figma-07.png\" />\n </div>\n\n ### Layout\n\n Banners can use either a Standalone layout with rounded corners for use within other elements, or a Bleed layout without border radius for full-width placement.\n\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Banner Standalone layout\" src=\"/components/banner/guidelines/figma-08.png\" />\n <img alt=\"Banner Bleed layout\" src=\"/components/banner/guidelines/figma-09.png\" />\n </div>\n\n ### Size\n\n Banners support two sizes: Small (default) with 12px body text, and Medium with 14px text and added padding for more prominent messages.\n\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Banner Small size\" src=\"/components/banner/guidelines/figma-10.png\" />\n <img alt=\"Banner Medium size\" src=\"/components/banner/guidelines/figma-11.png\" />\n </div>\n\n ## Behaviour\n\n ### Close action\n\n Banners can include a close button allowing users to dismiss the message. This is especially useful for informational or non-critical messages.\n\n <div class=\"not-prose\"><img alt=\"Banner with close button\" src=\"/components/banner/guidelines/figma-12.png\" /></div>\n\n ## Usage Examples\n\n ### File Manager β€” File Deletion Flow\n\n The Info banner informs users about the effects of file deletion, helping them understand the consequences of their action.\n\n <div class=\"not-prose\"><img alt=\"Banner in file deletion flow\" src=\"/components/banner/guidelines/figma-13.png\" /></div>\n\n ### Plugin Visibility Settings\n\n Users with limited permissions see an Info banner explaining that content is visible to users and groups they cannot see.\n\n <div class=\"not-prose\"><img alt=\"Banner for plugin visibility\" src=\"/components/banner/guidelines/figma-14.png\" /></div>\n\n ### Branding: Custom Fonts\n\n Banners can be customized with additional styling while maintaining their core message communication purpose.\n\n <div class=\"not-prose\"><img alt=\"Banner with custom styling\" src=\"/components/banner/guidelines/figma-15.png\" /></div>\n\n </Fragment>\n</DocsTabs>"
15
+ },
16
+ {
17
+ "slug": "components/bottom-sheet",
18
+ "title": "Bottom Sheet",
19
+ "description": "Bottom sheets surface content or actions from the bottom of the viewport, typically on smaller screens.",
20
+ "group": "Components",
21
+ "body": "<ComponentPreview>\n <BottomSheetBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {BottomSheet, Button} from '@staffbase/design';\n\n<BottomSheet.Root>\n <BottomSheet.Trigger>\n <Button>Open BottomSheet</Button>\n </BottomSheet.Trigger>\n <BottomSheet.Popup>\n <BottomSheet.Header>\n <BottomSheet.Back aria-label=\"Back\" />\n <BottomSheet.Title>Sheet title</BottomSheet.Title>\n <BottomSheet.Description>Sheet description.</BottomSheet.Description>\n <BottomSheet.Close aria-label=\"Close\" />\n </BottomSheet.Header>\n <BottomSheet.Action>Confirm</BottomSheet.Action>\n </BottomSheet.Popup>\n</BottomSheet.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {BottomSheet} from '@staffbase/design';\n\n<BottomSheet.Root>\n <BottomSheet.Trigger />\n <BottomSheet.Popup>\n <BottomSheet.Header>\n <BottomSheet.Back />\n <BottomSheet.Title />\n <BottomSheet.Description />\n <BottomSheet.Close />\n </BottomSheet.Header>\n <BottomSheet.Action />\n </BottomSheet.Popup>\n</BottomSheet.Root>\n```\n</Snippet>\n\n## API reference\n\n### Root\n\n<BuiltOn url=\"https://base-ui.com/react/components/drawer#root\">Base UI Drawer Root</BuiltOn>\n\nGroups all parts of the bottom sheet and manages its open state.\n\n### Trigger\n\n<BuiltOn url=\"https://base-ui.com/react/components/drawer#trigger\">Base UI Drawer Trigger</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'Opens the bottom sheet when activated. Must be a button element.',\n },\n ]}\n/>\n\n### Popup\n\n<BuiltOn url=\"https://base-ui.com/react/components/drawer#popup\">Base UI Drawer Popup</BuiltOn>\n\nRenders the sheet surface, including the backdrop, drag handle, and content area.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The content of the bottom sheet.',\n },\n {\n name: 'container',\n type: ['HTMLElement', 'RefObject<HTMLElement>'],\n description:\n 'Use this instead of z-index to specify another parent element to render the sheet portal into. Default is the document body.',\n },\n ]}\n/>\n\n### Header\n\nRenders a `<div>` that lays out the back button, title, description, and close button.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The header content, typically a title, description, and close or back buttons.',\n },\n ]}\n/>\n\n### Back\n\nRenders a `<button>` with a leading caret icon for navigating to a previous step.\n\n<PropTable\n rows={[\n {\n name: 'aria-label',\n type: 'string',\n required: true,\n description: 'An accessible label for the back button, since it has no visible text.',\n },\n ]}\n/>\n\n### Title\n\n<BuiltOn url=\"https://base-ui.com/react/components/drawer#title\">Base UI Drawer Title</BuiltOn>\n\nRenders an `<h2>` element that labels the bottom sheet.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'A heading that labels the bottom sheet.',\n },\n ]}\n/>\n\n### Description\n\n<BuiltOn url=\"https://base-ui.com/react/components/drawer#description\">Base UI Drawer Description</BuiltOn>\n\nRenders a `<p>` element with additional information about the bottom sheet.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'A description that provides additional context for the bottom sheet.',\n },\n ]}\n/>\n\n### Close\n\n<BuiltOn url=\"https://base-ui.com/react/components/drawer#close\">Base UI Drawer Close</BuiltOn>\n\nRenders a `<button>` with a close icon that dismisses the bottom sheet.\n\n<PropTable\n rows={[\n {\n name: 'aria-label',\n type: 'string',\n required: true,\n description: 'An accessible label for the close button, since it has no visible text.',\n },\n ]}\n/>\n\n### Action\n\nRenders a `<button>` for the primary action of the bottom sheet.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The label of the action button.',\n },\n ]}\n/>"
22
+ },
23
+ {
24
+ "slug": "components/button",
25
+ "title": "Button",
26
+ "description": "Buttons trigger an action or event, such as submitting a form, opening a dialog, or performing a destructive action.",
27
+ "group": "Components",
28
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n <ComponentPreview>\n <Button variant=\"solid\" color=\"primary\">Button</Button>\n <Fragment slot=\"code\">\n```tsx\nimport {Button} from '@staffbase/design';\n\n<Button variant=\"solid\" color=\"primary\">Button</Button>\n```\n </Fragment>\n </ComponentPreview>\n\n ## Anatomy\n\n <Snippet>\n\n```tsx\nimport {Button} from '@staffbase/design';\n\n<Button />;\n```\n\n </Snippet>\n\n ## Examples\n\n ### Variants\n\n <ComponentPreview>\n <div class=\"flex flex-wrap gap-2\">\n <Button variant=\"solid\">Solid</Button>\n <Button variant=\"outline\">Outline</Button>\n <Button variant=\"ghost\">Ghost</Button>\n </div>\n <Fragment slot=\"code\">\n\n```tsx\nimport {Button} from '@staffbase/design';\n\n<Button variant=\"solid\">Solid</Button>\n<Button variant=\"outline\">Outline</Button>\n<Button variant=\"ghost\">Ghost</Button>\n```\n\n </Fragment>\n </ComponentPreview>\n\n ### Colors\n\n <ComponentPreview>\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex flex-wrap gap-2\">\n <Button color=\"primary\">Primary</Button>\n <Button color=\"neutral\">Neutral</Button>\n <Button color=\"critical\">Critical</Button>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n <Button variant=\"outline\" color=\"primary\">Primary</Button>\n <Button variant=\"outline\" color=\"neutral\">Neutral</Button>\n <Button variant=\"outline\" color=\"critical\">Critical</Button>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n <Button variant=\"ghost\" color=\"primary\">Primary</Button>\n <Button variant=\"ghost\" color=\"neutral\">Neutral</Button>\n <Button variant=\"ghost\" color=\"critical\">Critical</Button>\n </div>\n </div>\n <Fragment slot=\"code\">\n\n```tsx\nimport {Button} from '@staffbase/design';\n\n{/* Solid */}\n<Button color=\"primary\">Primary</Button>\n<Button color=\"neutral\">Neutral</Button>\n<Button color=\"critical\">Critical</Button>\n\n{/* Outline */}\n<Button variant=\"outline\" color=\"primary\">Primary</Button>\n<Button variant=\"outline\" color=\"neutral\">Neutral</Button>\n<Button variant=\"outline\" color=\"critical\">Critical</Button>\n\n{/* Ghost */}\n<Button variant=\"ghost\" color=\"primary\">Primary</Button>\n<Button variant=\"ghost\" color=\"neutral\">Neutral</Button>\n<Button variant=\"ghost\" color=\"critical\">Critical</Button>\n```\n\n </Fragment>\n </ComponentPreview>\n\n ### With icons\n\n <ComponentPreview>\n <div class=\"flex flex-wrap gap-2\">\n <Button>Continue <ArrowRightIcon /></Button>\n <Button disabled><LoadingSpinner size={12} />Saving...</Button>\n <Button variant=\"outline\" color=\"neutral\"><AddIcon />Add item</Button>\n </div>\n <Fragment slot=\"code\">\n\n```tsx\nimport {Button, ArrowRightIcon, AddIcon, LoadingSpinner} from '@staffbase/design';\n\n<Button>Continue <ArrowRightIcon /></Button>\n<Button><LoadingSpinner />Saving...</Button>\n<Button variant=\"outline\" color=\"neutral\"><AddIcon />Add item</Button>\n```\n\n </Fragment>\n </ComponentPreview>\n\n ### Icon only\n\n Use `iconOnly` to make the padding even so the button renders as a square.\n\n <ComponentPreview>\n <div class=\"flex flex-wrap gap-2\">\n <Button iconOnly aria-label=\"Add\"><AddIcon /></Button>\n <Button variant=\"outline\" color=\"neutral\" iconOnly aria-label=\"Add\"><AddIcon /></Button>\n <Button variant=\"ghost\" color=\"neutral\" iconOnly aria-label=\"Add\"><AddIcon /></Button>\n </div>\n <Fragment slot=\"code\">\n\n```tsx\nimport {Button, AddIcon} from '@staffbase/design';\n\n<Button iconOnly aria-label=\"Add\"><AddIcon /></Button>\n<Button variant=\"outline\" color=\"neutral\" iconOnly aria-label=\"Add\"><AddIcon /></Button>\n<Button variant=\"ghost\" color=\"neutral\" iconOnly aria-label=\"Add\"><AddIcon /></Button>\n```\n\n </Fragment>\n </ComponentPreview>\n\n ### With tooltip\n\n Use `Tooltip` with `is=\"label\"` to provide an accessible name for icon-only buttons. The tooltip text becomes the button's label for assistive technology.\n\n <ComponentPreview>\n <ButtonWithTooltip client:load />\n <Fragment slot=\"code\">\n\n```tsx\nimport {Button, SettingsIcon, Tooltip} from '@staffbase/design';\n\n<Tooltip.Root>\n <Tooltip.Trigger>\n <Button iconOnly>\n <SettingsIcon />\n </Button>\n </Tooltip.Trigger>\n <Tooltip.Content is=\"label\" side=\"bottom\">\n Settings\n </Tooltip.Content>\n</Tooltip.Root>;\n```\n\n </Fragment>\n </ComponentPreview>\n\n ## API reference\n\n ### Button\n\n <PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n description: 'The content of the button. Can be text, icons, or both.',\n required: true,\n },\n {\n name: 'variant',\n type: ['solid', 'outline', 'ghost'],\n description: 'The visual style of the button.',\n default: 'solid',\n },\n {\n name: 'color',\n type: ['primary', 'neutral', 'critical'],\n description: 'The color scheme of the button.',\n default: 'primary',\n },\n {\n name: 'iconOnly',\n type: 'boolean',\n description: 'Makes the button square with even padding, for icon-only buttons. Pair with aria-label for accessibility.',\n default: 'false',\n },\n ]}\n />\n\n </Fragment>\n\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n Use the button anatomy examples to understand how labels, icons, and visual hierarchy work together. The visuals in the Figma frame show the default structure and the most common button arrangements.\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n\n ![Button anatomy visual example](/components/button/guidelines/figma-01.png)\n\n </div>\n <div>\n\n ![Button anatomy alternative visual example](/components/button/guidelines/figma-02.png)\n\n </div>\n </div>\n\n ![Button anatomy additional visual example](/components/button/guidelines/figma-03.png)\n\n ![Button anatomy extended visual example](/components/button/guidelines/figma-04.png)\n\n ## Properties\n\n ### Action Type\n\n Button variants are designed to communicate action type through color and emphasis. Keep those colors intact so the hierarchy stays clear and accessible.\n\n ![Button action type examples](/components/button/guidelines/figma-05.png)\n\n ### Icons and Labels\n\n Use a leading or trailing icon when it helps clarify the action. For icon-only buttons, provide a tooltip so the action is still understandable.\n\n ![Button icon and label examples](/components/button/guidelines/figma-06.png)\n\n ### Disabled\n\n Use disabled state to show that an action exists but is not currently available. Preserve the layout so the action remains discoverable.\n\n ![Button disabled examples](/components/button/guidelines/figma-07.png)\n\n ### Loading\n\n Loading is a custom implementation for now. When an action is in progress, update the label so users can see that the action is being carried out.\n\n ![Button loading examples](/components/button/guidelines/figma-08.png)\n\n ### Links\n\n Buttons can act as links when a text link does not carry enough prominence. Use this pattern only when the button treatment improves clarity.\n\n ![Button link examples](/components/button/guidelines/figma-09.png)\n\n ## Behaviour\n\n ### Interaction States\n\n Hover, press, focus, and keyboard interaction should all provide visible feedback. The Figma examples show the same interaction logic for mouse and keyboard users.\n\n ![Button interaction state examples](/components/button/guidelines/figma-10.png)\n\n ![Button interaction state detail examples](/components/button/guidelines/figma-11.png)\n\n ## Best Practices\n\n ### Use only one Primary button per screen\n\n Use a single primary action to preserve hierarchy. Multiple primary buttons on one screen make the most important action harder to identify.\n\n ![Single primary button best practice](/components/button/guidelines/figma-12.png)\n\n ### Use spacing.8 between buttons in a group\n\n Keep grouped buttons consistently spaced so action clusters read as a set.\n\n ![Button group spacing example](/components/button/guidelines/figma-13.png)\n\n ### Provide a tooltip label for Icon only buttons\n\n Icon-only buttons need an explicit label in a tooltip so users can still understand the action.\n\n ![Icon-only button tooltip example](/components/button/guidelines/figma-14.png)\n\n ### Don't use Icons as decoration\n\n Use icons only when they add meaning. Decorative icons weaken the label and make the action harder to read.\n\n ![Button icon decoration warning example](/components/button/guidelines/figma-15.png)\n\n ### Don't use Ghost Buttons on neutral backgrounds or images\n\n Ghost buttons need enough contrast to remain legible. Avoid placing them on neutral base backgrounds or busy imagery.\n\n ![Ghost button contrast warning example](/components/button/guidelines/figma-16.png)\n\n ### Do disabled Buttons need contrast?\n\n Disabled elements do not have the same contrast requirement as active UI, but the state should still be clear and communicated through semantics.\n\n ### Additional Do and Don't Examples\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n\n ![Button do and don't example 1](/components/button/guidelines/figma-18.png)\n\n ![Button do and don't example 2](/components/button/guidelines/figma-19.png)\n\n ![Button do and don't example 3](/components/button/guidelines/figma-20.png)\n\n ![Button do and don't example 4](/components/button/guidelines/figma-21.png)\n\n ![Button do and don't example 5](/components/button/guidelines/figma-22.png)\n\n ![Button do and don't example 6](/components/button/guidelines/figma-23.png)\n\n ![Button do and don't example 7](/components/button/guidelines/figma-24.png)\n\n ![Button do and don't example 8](/components/button/guidelines/figma-25.png)\n\n </div>\n\n ## Content Guidelines\n\n ### Header/Title\n\n Use Title Case for button labels.\n\n ![Button content guideline card](/components/button/guidelines/figma-17.png)\n\n ### Content\n\n Write concise, action-oriented labels. Prefer verbs such as Create, Add, Move, Publish, Save, Back, or Cancel when they match the user flow.\n\n Keep labels short, avoid unnecessary words, avoid ending punctuation, and use a trailing ellipsis for loading states when the action is still in progress.\n\n ## Usage Examples\n\n ### Studio Header\n\n Use the primary button for the main action in a screen header, such as Delete or Create, and keep the secondary action clearly subordinate.\n\n ![Studio header button usage example](/components/button/guidelines/figma-26.png)\n\n ### Dashboard Sidebar - Trailing Icon\n\n Use a trailing icon when the icon reinforces the button label, such as an action that opens another destination or expands a menu.\n\n ![Dashboard sidebar trailing icon usage example](/components/button/guidelines/figma-27.png)\n\n </Fragment>\n</DocsTabs>"
29
+ },
30
+ {
31
+ "slug": "components/card",
32
+ "title": "Card",
33
+ "description": "Cards group related content and actions into a single, contained surface.",
34
+ "group": "Components",
35
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n <ComponentPreview>\n <Card className='p-3'>\n <h3 className='text-title-md font-bold'>Title</h3>\n <p>This is a card containing related content.</p>\n </Card>\n <Fragment slot=\"code\">\n```tsx\nimport {Card} from '@staffbase/design';\n\n<Card className='p-3'>\n <h3 className='text-title-md font-bold'>Title</h3>\n <p>This is a card containing related content.</p>\n</Card>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Card} from '@staffbase/design';\n\n<Card />\n```\n</Snippet>\n\n## API reference\n\n### Card\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n description: 'The content of the card.',\n },\n ]}\n/>\n </Fragment>\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n Cards provide a contained surface for grouping related content. They use a consistent border radius and can accommodate various content types while maintaining visual hierarchy.\n\n <div class=\"grid gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Card anatomy full example\" src=\"/components/card/guidelines/figma-01.png\" class=\"md:col-span-2\" />\n <img alt=\"Card anatomy example 1\" src=\"/components/card/guidelines/figma-02.png\" />\n <img alt=\"Card anatomy example 2\" src=\"/components/card/guidelines/figma-03.png\" />\n </div>\n\n ## Properties\n\n ### Padding\n\n Cards can use any padding following the 8px grid for consistency. The amount of padding can be adjusted based on content requirements and visual hierarchy needs.\n\n <div class=\"not-prose\"><img alt=\"Card padding options\" src=\"/components/card/guidelines/figma-04.png\" /></div>\n\n ## Best Practices\n\n ### Do not deviate from default styling\n\n Maintain card consistency by not adding borders, shadows, or custom styling. Keep cards consistent with other cards in your layout.\n\n <div class=\"not-prose\"><img alt=\"Card styling consistency\" src=\"/components/card/guidelines/figma-05.png\" /></div>\n\n ### Cards should only be placed on Base backgrounds\n\n Place cards exclusively on the neutral base background. Avoid placing cards on other background colors to maintain visual clarity.\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Card on base background\" src=\"/components/card/guidelines/figma-06.png\" />\n <img alt=\"Card incorrect placement\" src=\"/components/card/guidelines/figma-07.png\" />\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Do not use cards as flyouts or modals\n\n Cards are intended as static elements directly on page backgrounds. Use Dialog or Popover components for interactive overlays instead.\n\n </div>\n <div class=\"not-prose\"><img alt=\"Card as static element\" src=\"/components/card/guidelines/figma-08.png\" /></div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Inner elements should use borderRadius.6\n\n The Card component has a Border Radius of 8px. Inner elements should use borderRadius.6 to maintain visual hierarchy.\n\n </div>\n <div class=\"not-prose\"><img alt=\"Card inner element border radius\" src=\"/components/card/guidelines/figma-09.png\" /></div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Space Cards at a minimum of 24px\n\n When placing multiple cards adjacent to each other, maintain a minimum 24px spacing for visual separation.\n\n </div>\n <div class=\"not-prose\"><img alt=\"Card spacing between cards\" src=\"/components/card/guidelines/figma-10.png\" /></div>\n </div>\n </div>\n\n ## Usage Examples\n\n ### Studio Dashboard\n\n Cards are used on the Studio Dashboard to section and contain highlighted insights and information from different content areas.\n\n <div class=\"not-prose\"><img alt=\"Card usage in dashboard\" src=\"/components/card/guidelines/figma-11.png\" /></div>\n\n </Fragment>\n</DocsTabs>"
36
+ },
37
+ {
38
+ "slug": "components/dialog",
39
+ "title": "Dialog",
40
+ "description": "Dialogs present focused content or actions in a modal overlay above the main page.",
41
+ "group": "Components",
42
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n<ComponentPreview>\n <DialogBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Button, CloseIcon, Dialog} from '@staffbase/design';\n\n<Dialog.Root>\n <Dialog.Trigger>\n <Button>Open Dialog</Button>\n </Dialog.Trigger>\n <Dialog.Popup>\n <Dialog.Header>\n <Dialog.Title>Dialog title</Dialog.Title>\n <Dialog.Close>\n <Button variant=\"ghost\" color=\"neutral\" iconOnly aria-label=\"Close\"><CloseIcon /></Button>\n </Dialog.Close>\n </Dialog.Header>\n <Dialog.Body>\n <Dialog.Description>This is a description of the dialog content.</Dialog.Description>\n </Dialog.Body>\n <Dialog.Footer>\n <Dialog.Close>\n <Button color=\"neutral\">Cancel</Button>\n </Dialog.Close>\n <Button>Confirm</Button>\n </Dialog.Footer>\n </Dialog.Popup>\n</Dialog.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Dialog} from '@staffbase/design';\n\n<Dialog.Root>\n <Dialog.Trigger />\n <Dialog.Popup>\n <Dialog.Header>\n <Dialog.Title />\n <Dialog.Close />\n </Dialog.Header>\n <Dialog.Body>\n <Dialog.Description />\n </Dialog.Body>\n <Dialog.Footer />\n </Dialog.Popup>\n</Dialog.Root>\n```\n</Snippet>\n\n## API reference\n\n### Root\n\n<BuiltOn url=\"https://base-ui.com/react/components/dialog#root\">Base UI Dialog Root</BuiltOn>\n\nGroups all parts of the dialog and manages its open state.\n\n### Trigger\n\n<BuiltOn url=\"https://base-ui.com/react/components/dialog#trigger\">Base UI Dialog Trigger</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'Opens the dialog when activated. Passed as a single button element, not via a render prop.',\n },\n ]}\n/>\n\n### Close\n\n<BuiltOn url=\"https://base-ui.com/react/components/dialog#close\">Base UI Dialog Close</BuiltOn>\n\nDismisses the dialog when activated.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The element that closes the dialog. Passed as a single button element, not via a render prop.',\n },\n ]}\n/>\n\n### Popup\n\n<BuiltOn url=\"https://base-ui.com/react/components/dialog#popup\">Base UI Dialog Popup</BuiltOn>\n\nRenders the modal surface and its backdrop, portalled out of the DOM flow.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The content of the dialog, typically a header, body, and footer.',\n },\n {\n name: 'container',\n type: ['HTMLElement', 'RefObject<HTMLElement>'],\n description:\n 'Use this instead of z-index to specify another parent element to render the dialog portal into. Default is the document body.',\n },\n ]}\n/>\n\n### Header\n\nRenders a `<div>` that lays out the title and close button.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The header content, typically a title and a close button.',\n },\n ]}\n/>\n\n### Body\n\nRenders a `<div>` for the main content of the dialog.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The main content of the dialog.',\n },\n ]}\n/>\n\n### Footer\n\nRenders a `<div>` that lays out the dialog's actions.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The footer content, typically action buttons.',\n },\n ]}\n/>\n\n### Title\n\n<BuiltOn url=\"https://base-ui.com/react/components/dialog#title\">Base UI Dialog Title</BuiltOn>\n\nRenders an `<h2>` element that labels the dialog.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'A heading that labels the dialog.',\n },\n ]}\n/>\n\n### Description\n\n<BuiltOn url=\"https://base-ui.com/react/components/dialog#description\">Base UI Dialog Description</BuiltOn>\n\nA visually hidden element available and recommended to use.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'A description that provides additional context for the dialog.',\n },\n ]}\n/>\n\n </Fragment>\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n Use the anatomy examples to keep dialogs structured around a clear title, optional controls in the header, a scrollable content area when needed, and a footer for actions.\n\n ![Dialog anatomy visual example](/components/dialog/guidelines/figma-01.png)\n\n ![Dialog anatomy full layout example](/components/dialog/guidelines/figma-02.png)\n\n ![Dialog anatomy dark theme layout example](/components/dialog/guidelines/figma-03.png)\n\n ## Properties\n\n ### Header Close Button\n\n A dialog header can include an optional close button. Use it only when it does not compete with a footer action pattern that already provides a clear way out.\n\n ![Dialog header close button example](/components/dialog/guidelines/figma-04.png)\n\n ### Header Button(s)\n\n Header buttons are useful for tasks such as upload or other context-specific actions that belong next to the title.\n\n ![Dialog header button example](/components/dialog/guidelines/figma-05.png)\n\n ### Header Content\n\n Use header content for sticky filters or sorting options when those controls need to stay visible while the dialog body scrolls.\n\n ![Dialog header content example](/components/dialog/guidelines/figma-06.png)\n\n ### Scrolling\n\n Enable scrolling when the dialog content exceeds the available viewport. Keep the top-level structure stable so the title and actions remain easy to find.\n\n ![Dialog scrolling example](/components/dialog/guidelines/figma-07.png)\n\n ### Footer\n\n The footer holds the main dialog actions and should remain visible when the content area scrolls.\n\n ![Dialog footer example](/components/dialog/guidelines/figma-08.png)\n\n ### Footer Content\n\n Use footer content for action state or supporting metadata when that information helps users complete the task.\n\n ![Dialog footer content example](/components/dialog/guidelines/figma-09.png)\n\n ## Behaviour\n\n ### Breakpoints\n\n From 0 to 480px, the dialog is full width and full height with no border radius. From 480px and up, it uses 88% width, a maximum width of 800px, and a height that wraps the content with a maximum height of 88%.\n\n ![Dialog breakpoint behavior examples](/components/dialog/guidelines/figma-10.png)\n\n ![Dialog breakpoint behavior alternative example](/components/dialog/guidelines/figma-11.png)\n\n ## Best Practices\n\n ### More modals = More problems\n\n **The best modal is a new page.** Modal dialogs are often misused. All too often they contain too much content that would be better off as a new page. This improves performance and doesn't break the back button like a dialog often does.\n\n ### When to use a dialog?\n\n Use dialogs for focused tasks that require immediate action. Keep the purpose clear, limit the amount of content, and avoid turning a dialog into a long multi-step flow.\n\n Do not put a modal on top of a modal. If the task gets more complex, consider a multistep flow instead.\n\n ### Closing a dialog\n\n Include at least one close option. Use a clear Close or Cancel action, and avoid duplicating close controls in both the header and footer unless the pattern explicitly requires it.\n\n ![Dialog close option do and don't examples](/components/dialog/guidelines/figma-12.png)\n\n ### Avoid duplicated close actions\n\n Avoid placing duplicate close controls in both header and footer when one clear close option is already present.\n\n ![Dialog duplicate close action warning example](/components/dialog/guidelines/figma-13.png)\n\n ## Content Guidelines\n\n ### Header/Title\n\n Use Title Case for dialog titles.\n\n ### Content\n\n Keep dialog content concise and relevant. Follow the same writing standards you already use for field labels, inline error messages, buttons, and other content inside the modal.\n\n </Fragment>\n</DocsTabs>"
43
+ },
44
+ {
45
+ "slug": "components/field",
46
+ "title": "Field",
47
+ "description": "Fields group form controls with labels, descriptions, and validation messages to provide clear context and accessibility.",
48
+ "group": "Components",
49
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n<ComponentPreview>\n <FieldBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, TextField} from '@staffbase/design';\n\n<Field.Root className=\"max-w-75\">\n <Field.Label>Name</Field.Label>\n <Field.Description>Enter your full name.</Field.Description>\n <TextField />\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Field, TextField} from '@staffbase/design';\n\n<Field.Root>\n <Field.Label />\n <Field.Description />\n {/* Form control goes here */}\n <Field.Error />\n</Field.Root>\n```\n</Snippet>\n\n## Examples\n\n### Required label\n\n<ComponentPreview>\n <FieldRequiredLabel client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, TextField} from '@staffbase/design';\n\n<Field.Root className=\"max-w-75\">\n <Field.Label requiredLabel=\"Required\">Email</Field.Label>\n <TextField type=\"email\" aria-required />\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n### Invalid state\n\n<ComponentPreview>\n <FieldInvalid client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, TextField} from '@staffbase/design';\n\n<Field.Root invalid className=\"max-w-75\">\n <Field.Label>Password</Field.Label>\n <TextField type=\"password\" />\n <Field.Error match>Password must be at least 8 characters.</Field.Error>\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## API reference\n\n### Root\n\n<BuiltOn url=\"https://base-ui.com/react/components/field#root\">Base UI Field Root</BuiltOn>\n\nProvides field context that links labels, descriptions, and error messages to form controls.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The field content, usually a label, control, optional description, and optional error.',\n },\n ]}\n/>\n\n### Label\n\n<BuiltOn url=\"https://base-ui.com/react/components/field#label\">Base UI Field Label</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The visible label text for the associated control.',\n },\n {\n name: 'requiredLabel',\n type: 'string',\n description: 'Optional text rendered in parentheses after the label to indicate required input.',\n },\n ]}\n/>\n\n### Description\n\n<BuiltOn url=\"https://base-ui.com/react/components/field#description\">Base UI Field Description</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'Supplementary helper text that is announced with the associated control.',\n },\n ]}\n/>\n\n### Error\n\n<BuiltOn url=\"https://base-ui.com/react/components/field#error\">Base UI Field Error</BuiltOn>\n\nDisplays an error message and prepends an alert icon.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The error message content.',\n },\n ]}\n/>\n </Fragment>\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n Fields combine labels, helper content, form controls, and validation feedback into one accessible structure.\n\n <div class=\"space-y-6 not-prose\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <img alt=\"Field anatomy example 1\" src=\"/components/field/guidelines/figma-01.png\" />\n <img alt=\"Field anatomy example 2\" src=\"/components/field/guidelines/figma-02.png\" />\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <img alt=\"Field anatomy example 3\" src=\"/components/field/guidelines/figma-03.png\" />\n <img alt=\"Field anatomy example 4\" src=\"/components/field/guidelines/figma-04.png\" />\n </div>\n </div>\n\n ## Properties\n\n Field properties define spacing and element composition so labels, descriptions, controls, and errors stay readable and consistent.\n\n <div class=\"not-prose\"><img alt=\"Field properties overview\" src=\"/components/field/guidelines/figma-05.png\" /></div>\n\n ## Behaviour\n\n Field behaviour should clearly communicate state changes and maintain context during validation and correction flows.\n\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Field behaviour example 1\" src=\"/components/field/guidelines/figma-06.png\" />\n <img alt=\"Field behaviour example 2\" src=\"/components/field/guidelines/figma-07.png\" />\n </div>\n\n ## Content Guidelines\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Field Label\n\n <ul>\n <li>Use Title Case with nouns or noun phrases.</li>\n <li>Do not use a colon or ending punctuation.</li>\n <li>For required inputs, use <strong>(Required)</strong> consistently.</li>\n <li>Use <strong>(Optional)</strong> only when optional fields are the exception.</li>\n </ul>\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Field label guidelines\" src=\"/components/field/guidelines/figma-08.png\" />\n </div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Placeholder Text\n\n Use placeholder text only when it adds value beyond labels and descriptions. Prefer brief examples or action-led instructions, and avoid repeating the label.\n\n Do not rely on placeholder text for instructions users need after they begin typing. Use description text for persistent guidance.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Placeholder text guidelines\" src=\"/components/field/guidelines/figma-09.png\" />\n </div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Description Text\n\n Use sentence case and end with a period. Keep guidance short, specific, and directly relevant to successful field completion.\n\n Avoid repeating the label. Use descriptions for critical context that helps prevent user errors.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Description text guidelines\" src=\"/components/field/guidelines/figma-10.png\" />\n </div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Error Message\n\n Use sentence case with a period and avoid exclamation points. Error text should clearly explain what is wrong and how to fix it.\n\n If description text is shown as an error, avoid showing both simultaneously to reduce noise.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Error message guidelines\" src=\"/components/field/guidelines/figma-11.png\" />\n </div>\n </div>\n </div>\n\n </Fragment>\n</DocsTabs>"
50
+ },
51
+ {
52
+ "slug": "components/loading-spinner",
53
+ "title": "Loading Spinner",
54
+ "description": "Loading spinners indicate that content is loading or an action is in progress.",
55
+ "group": "Components",
56
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n <ComponentPreview>\n <LoadingSpinner aria-label=\"Loading...\" size={48} />\n <Fragment slot=\"code\">\n```tsx\nimport {LoadingSpinner} from '@staffbase/design';\n\n<LoadingSpinner aria-label=\"Loading...\" size={48} />\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {LoadingSpinner} from '@staffbase/design';\n\n<LoadingSpinner />\n```\n</Snippet>\n\n## Examples\n\n### Sizes\n\n<ComponentPreview>\n <div style={{display: 'flex', alignItems: 'center', gap: '24px'}}>\n <LoadingSpinner aria-label=\"Loading\" size={12} />\n <LoadingSpinner aria-label=\"Loading\" size={24} />\n <LoadingSpinner aria-label=\"Loading\" size={48} />\n <LoadingSpinner aria-label=\"Loading\" size={64} />\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {LoadingSpinner} from '@staffbase/design';\n\n<LoadingSpinner aria-label=\"Loading\" size={12} />\n<LoadingSpinner aria-label=\"Loading\" size={24} />\n<LoadingSpinner aria-label=\"Loading\" size={48} />\n<LoadingSpinner aria-label=\"Loading\" size={64} />\n```\n </Fragment>\n</ComponentPreview>\n\n### With progress\n\n<ComponentPreview>\n <LoadingSpinner aria-label=\"Uploading\" size={48} progress={40} />\n <Fragment slot=\"code\">\n```tsx\nimport {LoadingSpinner} from '@staffbase/design';\n\n<LoadingSpinner aria-label=\"Uploading\" size={48} progress={40} />\n```\n </Fragment>\n</ComponentPreview>\n\n## API reference\n\n<PropTable\n rows={[\n {\n name: 'aria-label',\n type: 'string',\n description:\n 'Describes the process that the progress spinner refers to. This will be read with the progress status eg. \"Uploading 40%\" if used with a progress value.',\n required: true,\n },\n {\n name: 'size',\n type: ['12', '24', '48', '64'],\n description: 'Defines the size of the Progress Spinner.',\n default: '48',\n },\n {\n name: 'progress',\n type: 'number',\n description: 'Progress percentage. Values must always be between 0β€”100.',\n },\n ]}\n/>\n </Fragment>\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n The loading spinner is a circular indicator with a rotating progress ring. It communicates to users that content is loading or processing.\n\n <div class=\"space-y-8 not-prose\">\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2\">\n <img alt=\"Loading spinner anatomy example 1\" src=\"/components/loading-spinner/guidelines/figma-01.png\" />\n <img alt=\"Loading spinner anatomy example 2\" src=\"/components/loading-spinner/guidelines/figma-02.png\" />\n </div>\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2\">\n <img alt=\"Loading spinner anatomy example 3\" src=\"/components/loading-spinner/guidelines/figma-03.png\" />\n <img alt=\"Loading spinner anatomy example 4\" src=\"/components/loading-spinner/guidelines/figma-04.png\" />\n </div>\n </div>\n\n ### Adjusting value to a custom number\n\n For simplicity, Figma examples may show predefined values, but the code component supports any progress value from 0 to 100.\n\n <div class=\"not-prose\"><img alt=\"Adjusting loading spinner progress value\" src=\"/components/loading-spinner/guidelines/figma-05.png\" /></div>\n\n ## Properties\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Size\n\n Progress Spinner comes in various sizes, similar to Loading Spinner. At smaller sizes, the number can't be shown any more and only visual indicator is provided.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Loading spinner sizes\" src=\"/components/loading-spinner/guidelines/figma-06.png\" />\n </div>\n </div>\n </div>\n\n ## Behaviour\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Progress: Animation\n\n Clockwise filling direction.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Loading spinner progress animation\" src=\"/components/loading-spinner/guidelines/figma-07.png\" />\n </div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Loading: Animation\n\n When visible, the Loading Spinner has an animated rotation to indicate an active loading state.\n\n See [Codepen](https://codepen.io/carol-c-han/pen/vYjvZNd).\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Loading spinner loading animation\" src=\"/components/loading-spinner/guidelines/figma-08.png\" />\n </div>\n </div>\n </div>\n\n ## Best Practices\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Loading Animation\n\n Use loading spinners for short loading times where remaining time is not relevant (e.g. uploading a small file like a custom icon or loading page content).\n\n When loading times are longer, e.g. for uploading video files, consider implementing a more expressive component that shows percentages and/or remaining time. (to be discovered)\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Loading spinner best practices\" src=\"/components/loading-spinner/guidelines/figma-09.png\" />\n </div>\n </div>\n </div>\n\n ## Usage Examples\n\n ### My Files / File Manager Thumbnail View\n\n Progress actions happening inside the thumbnail.\n\n <div class=\"not-prose\"><img alt=\"Loading spinner in thumbnail view\" src=\"/components/loading-spinner/guidelines/figma-10.png\" /></div>\n\n ### My Files / File Manager List View\n\n Progress actions happening in a list view.\n\n <div class=\"not-prose\"><img alt=\"Loading spinner in list view\" src=\"/components/loading-spinner/guidelines/figma-11.png\" /></div>\n\n ### File Upload Component\n\n Progress actions happening in file upload flows.\n\n <div class=\"not-prose\"><img alt=\"Loading spinner in file upload\" src=\"/components/loading-spinner/guidelines/figma-12.png\" /></div>\n\n </Fragment>\n</DocsTabs>"
57
+ },
58
+ {
59
+ "slug": "components/menu",
60
+ "title": "Menu",
61
+ "description": "Menus display a list of actions or options that a user can choose. They open from a trigger and close on selection.",
62
+ "group": "Components",
63
+ "body": "<ComponentPreview>\n <MenuBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {AddIcon, Button, Menu} from '@staffbase/design';\n\n<Menu.Root>\n <Menu.Trigger>\n <Button color=\"neutral\">Open menu</Button>\n </Menu.Trigger>\n <Menu.Popup>\n <Menu.Item leadingIcon={<AddIcon />} description=\"Save your favourites\">\n Add to Library\n </Menu.Item>\n <Menu.Item>Add to Playlist</Menu.Item>\n <Menu.Separator />\n <Menu.Item variant=\"critical\">Delete</Menu.Item>\n </Menu.Popup>\n</Menu.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Menu} from '@staffbase/design';\n\n<Menu.Root>\n <Menu.Trigger />\n <Menu.Popup>\n <Menu.Group>\n <Menu.GroupLabel />\n <Menu.Item />\n </Menu.Group>\n <Menu.Separator />\n <Menu.Item />\n </Menu.Popup>\n</Menu.Root>\n```\n</Snippet>\n\n## API reference\n\n### Root\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#root\">Base UI Menu Root</BuiltOn>\n\nGroups all parts of the menu and manages its open state. Rendered as non-modal by default.\n\n### Trigger\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#trigger\">Base UI Menu Trigger</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'Opens the menu when activated. Passed as a single button element, not via a render prop.',\n },\n ]}\n/>\n\n### Popup\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#positioner\">Base UI Menu Positioner</BuiltOn>\n\nRenders the menu surface, portalled out of the DOM flow and positioned against the trigger.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The menu items, groups, and separators.',\n },\n {\n name: 'side',\n type: ['top', 'bottom', 'left', 'right', 'inline-start', 'inline-end'],\n description: 'Which side of the trigger to position the menu against.',\n default: 'bottom',\n },\n {\n name: 'align',\n type: ['start', 'center', 'end'],\n description: 'How to align the menu against the trigger.',\n default: 'end',\n },\n {\n name: 'container',\n type: ['HTMLElement', 'RefObject<HTMLElement>'],\n description:\n 'Use this instead of z-index to specify another parent element to render the menu portal into. Default is the document body.',\n },\n ]}\n/>\n\n### Item\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#item\">Base UI Menu Item</BuiltOn>\n\nA selectable action in the menu.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The label of the menu item.',\n },\n {\n name: 'variant',\n type: ['neutral', 'critical'],\n description: 'The visual style of the item. Use \"critical\" for destructive actions.',\n default: 'neutral',\n },\n {\n name: 'leadingIcon',\n type: 'ReactNode',\n description: 'An icon rendered before the item label.',\n },\n {\n name: 'description',\n type: 'string',\n description: 'Secondary text rendered beneath the item label.',\n },\n ]}\n/>\n\n### Separator\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#separator\">Base UI Menu Separator</BuiltOn>\n\nRenders a dividing line between groups of items.\n\n### Group\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#group\">Base UI Menu Group</BuiltOn>\n\nGroups related items together, optionally labelled by a `GroupLabel`.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'A group label and the items it labels.',\n },\n ]}\n/>\n\n### GroupLabel\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#group-label\">Base UI Menu Group Label</BuiltOn>\n\nLabels a group of items.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The text that labels the group.',\n },\n ]}\n/>\n\n### RadioGroup\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#radio-group\">Base UI Menu Radio Group</BuiltOn>\n\nGroups a set of `RadioItem`s so that only one can be selected at a time.\n\n### RadioItem\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#radio-item\">Base UI Menu Radio Item</BuiltOn>\n\nA single selectable option within a `RadioGroup`. Closes the menu on selection.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The label of the radio item.',\n },\n {\n name: 'value',\n type: 'any',\n required: true,\n description: 'The value of the radio item, compared against the radio group value.',\n },\n ]}\n/>\n\n### SubMenuRoot\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#submenu-root\">Base UI Menu Submenu Root</BuiltOn>\n\nGroups a nested submenu and manages its open state. Wraps a `SubMenuTrigger` and a `Popup`.\n\n### SubMenuTrigger\n\n<BuiltOn url=\"https://base-ui.com/react/components/menu#submenu-trigger\">Base UI Menu Submenu Trigger</BuiltOn>\n\nAn item that opens a nested submenu. Renders with a trailing caret.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The label of the submenu trigger.',\n },\n {\n name: 'leadingIcon',\n type: 'ReactNode',\n description: 'An icon rendered before the trigger label.',\n },\n ]}\n/>\n\n## Examples\n\n### Grouped\n\n<ComponentPreview>\n <MenuGrouped client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Button, Menu} from '@staffbase/design';\n\n<Menu.Root>\n <Menu.Trigger>\n <Button color=\"neutral\">Open menu</Button>\n </Menu.Trigger>\n <Menu.Popup>\n <Menu.Group>\n <Menu.GroupLabel>Options</Menu.GroupLabel>\n <Menu.Item>Add to Library</Menu.Item>\n <Menu.Item>Add to Playlist</Menu.Item>\n </Menu.Group>\n <Menu.Separator />\n <Menu.Group>\n <Menu.GroupLabel>Controls</Menu.GroupLabel>\n <Menu.Item>Play Next</Menu.Item>\n </Menu.Group>\n </Menu.Popup>\n</Menu.Root>\n```\n </Fragment>\n</ComponentPreview>"
64
+ },
65
+ {
66
+ "slug": "components/number-stepper",
67
+ "title": "Number Stepper",
68
+ "description": "Number steppers let the user enter a numeric value with increment and decrement controls.",
69
+ "group": "Components",
70
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n\n<ComponentPreview>\n <NumberStepperBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, NumberStepper} from '@staffbase/design';\n\n<Field.Root>\n <Field.Label>Amount</Field.Label>\n <Field.Description>Enter a value between 0 and 10.</Field.Description>\n <NumberStepper.Root defaultValue={0} min={0} max={10}>\n <NumberStepper.Decrement />\n <NumberStepper.Input />\n <NumberStepper.Increment />\n </NumberStepper.Root>\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {NumberStepper} from '@staffbase/design';\n\n<NumberStepper.Root>\n <NumberStepper.Decrement />\n <NumberStepper.Input />\n <NumberStepper.Increment />\n</NumberStepper.Root>\n```\n</Snippet>\n\n## API reference\n\n### Root\n\n<BuiltOn url=\"https://base-ui.com/react/components/number-field#root\">Base UI Number Field Root</BuiltOn>\n\n### Increment\n\n<BuiltOn url=\"https://base-ui.com/react/components/number-field#increment\">Base UI Number Field Increment</BuiltOn>\n\n### Decrement\n\n<BuiltOn url=\"https://base-ui.com/react/components/number-field#decrement\">Base UI Number Field Decrement</BuiltOn>\n\n### Input\n\n<BuiltOn url=\"https://base-ui.com/react/components/number-field#input\">Base UI Number Field Input</BuiltOn>\n </Fragment>\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n Number steppers consist of a decrement button (minus), an input field for direct value entry, and an increment button (plus). All three elements are essential to the component.\n\n <div class=\"not-prose\"><img alt=\"Number stepper anatomy\" src=\"/components/number-stepper/guidelines/figma-01.png\" /></div>\n\n ## Behaviour\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose\">\n <div>\n <img alt=\"Number stepper button interactions\" src=\"/components/number-stepper/guidelines/figma-02.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Button Interactions</p>\n <p class=\"text-body-sm\">Rest Β· Hover Β· Pressed</p>\n </div>\n <div>\n <img alt=\"Number stepper min max button state\" src=\"/components/number-stepper/guidelines/figma-03.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Button Interactions</p>\n <p class=\"text-body-sm\">Enable/disabled buttons for min/max value</p>\n </div>\n <div>\n <img alt=\"Number stepper min max bounds\" src=\"/components/number-stepper/guidelines/figma-04.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Min-Max bounds</p>\n </div>\n <div>\n <img alt=\"Number stepper validation\" src=\"/components/number-stepper/guidelines/figma-05.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Validation</p>\n </div>\n <div>\n <img alt=\"Number stepper negative values\" src=\"/components/number-stepper/guidelines/figma-06.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Negative values</p>\n </div>\n <div>\n <img alt=\"Number stepper keyboard and focus\" src=\"/components/number-stepper/guidelines/figma-07.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Keyboard Input & Focus states</p>\n </div>\n <div>\n <img alt=\"Number stepper sizing\" src=\"/components/number-stepper/guidelines/figma-08.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Sizing</p>\n <p class=\"text-body-sm\">min-width: 128px<br />max-width: 240px</p>\n </div>\n <div>\n <img alt=\"Number stepper custom increment\" src=\"/components/number-stepper/guidelines/figma-09.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Custom increment</p>\n <p class=\"text-body-sm\">0 -> 5 -> 10 -> 15</p>\n </div>\n </div>\n\n ## Usage Examples\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose\">\n <div>\n <img alt=\"Number stepper usage example slideshow posts\" src=\"/components/number-stepper/guidelines/figma-10.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Screens / Layout / Posts per Slideshow</p>\n </div>\n <div>\n <img alt=\"Number stepper usage example slide duration\" src=\"/components/number-stepper/guidelines/figma-11.png\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Screens / Layout / Slide Duration</p>\n <p class=\"text-body-sm\">Second, increment of 5</p>\n </div>\n </div>\n\n ## Best Practices\n\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <div>\n <img alt=\"Number stepper best practice do\" src=\"/components/number-stepper/guidelines/figma-12.png?v=20260616-1052\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Do: Use for small numbers (&lt;10) for low interaction cost</p>\n </div>\n <div>\n <img alt=\"Number stepper best practice dont large values\" src=\"/components/number-stepper/guidelines/figma-13.png?v=20260616-1052\" />\n <p class=\"mt-3 text-body-sm font-semibold\">Don't: Input large values (&gt;20).</p>\n <p class=\"text-body-sm\">You can use a regular Text Input instead</p>\n </div>\n </div>\n\n <div class=\"mt-8 not-prose\">\n <img alt=\"Number stepper best practice dont full width\" src=\"/components/number-stepper/guidelines/figma-14.png?v=20260616-1052\" />\n </div>\n <p class=\"mt-3 text-body-sm font-semibold\">Don't: Set input to 100% width - Controls are too far apart from each other and the value</p>\n\n ## Content Guidelines\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Ranges\n\n Use an en dash (–) to indicate value ranges, for example **1–10**.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Number stepper range content guideline\" src=\"/components/number-stepper/guidelines/figma-15.png\" />\n </div>\n </div>\n </div>\n\n </Fragment></DocsTabs>"
71
+ },
72
+ {
73
+ "slug": "components/pill",
74
+ "title": "Pill",
75
+ "description": "Pills label, categorise, or tag content. Use them to highlight short status or metadata.",
76
+ "group": "Components",
77
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n <ComponentPreview>\n <Pill>Pill</Pill>\n <Fragment slot=\"code\">\n```tsx\nimport {Pill} from '@staffbase/design';\n\n<Pill>Pill</Pill>\n```\n </Fragment>\n </ComponentPreview>\n\n ## Anatomy\n\n <Snippet>\n\n```tsx\nimport {Pill} from '@staffbase/design';\n\n<Pill />;\n```\n\n </Snippet>\n\n ## Examples\n\n ### Variants\n\n <ComponentPreview>\n <div style={{display: 'flex', gap: '8px', flexWrap: 'wrap'}}>\n <Pill variant=\"solid\">Solid</Pill>\n <Pill variant=\"outline\">Outline</Pill>\n </div>\n <Fragment slot=\"code\">\n\n```tsx\nimport {Pill} from '@staffbase/design';\n\n<Pill variant=\"solid\">Solid</Pill>\n<Pill variant=\"outline\">Outline</Pill>\n```\n\n </Fragment>\n </ComponentPreview>\n\n ### Colors\n\n <ComponentPreview>\n <div style={{display: 'flex', gap: '8px', flexWrap: 'wrap'}}>\n <Pill color=\"primary\">Primary</Pill>\n <Pill color=\"critical\">Critical</Pill>\n <Pill color=\"success\">Success</Pill>\n <Pill color=\"warning\">Warning</Pill>\n <Pill color=\"neutral\">Neutral</Pill>\n <Pill color=\"teal\">Teal</Pill>\n <Pill color=\"purple\">Purple</Pill>\n <Pill color=\"pink\">Pink</Pill>\n <Pill color=\"cyan\">Cyan</Pill>\n <Pill color=\"yellow\">Yellow</Pill>\n <Pill variant='outline' color=\"primary\">Primary</Pill>\n <Pill variant='outline' color=\"critical\">Critical</Pill>\n <Pill variant='outline' color=\"success\">Success</Pill>\n <Pill variant='outline' color=\"warning\">Warning</Pill>\n <Pill variant='outline' color=\"neutral\">Neutral</Pill>\n <Pill variant='outline' color=\"teal\">Teal</Pill>\n <Pill variant='outline' color=\"purple\">Purple</Pill>\n <Pill variant='outline' color=\"pink\">Pink</Pill>\n <Pill variant='outline' color=\"cyan\">Cyan</Pill>\n <Pill variant='outline' color=\"yellow\">Yellow</Pill>\n </div>\n <Fragment slot=\"code\">\n\n```tsx\nimport {Pill} from '@staffbase/design';\n\n<Pill color=\"primary\">Primary</Pill>\n<Pill color=\"critical\">Critical</Pill>\n<Pill color=\"success\">Success</Pill>\n<Pill color=\"warning\">Warning</Pill>\n<Pill color=\"neutral\">Neutral</Pill>\n<Pill color=\"teal\">Teal</Pill>\n<Pill color=\"purple\">Purple</Pill>\n<Pill color=\"pink\">Pink</Pill>\n<Pill color=\"cyan\">Cyan</Pill>\n<Pill color=\"yellow\">Yellow</Pill>\n<Pill variant='outline' color=\"primary\">Primary</Pill>\n<Pill variant='outline' color=\"critical\">Critical</Pill>\n<Pill variant='outline' color=\"success\">Success</Pill>\n<Pill variant='outline' color=\"warning\">Warning</Pill>\n<Pill variant='outline' color=\"neutral\">Neutral</Pill>\n<Pill variant='outline' color=\"teal\">Teal</Pill>\n<Pill variant='outline' color=\"purple\">Purple</Pill>\n<Pill variant='outline' color=\"pink\">Pink</Pill>\n<Pill variant='outline' color=\"cyan\">Cyan</Pill>\n<Pill variant='outline' color=\"yellow\">Yellow</Pill>\n```\n\n </Fragment>\n </ComponentPreview>\n\n ## API reference\n\n <PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n description: 'Content rendered inside the pill.',\n required: true,\n },\n {\n name: 'variant',\n type: ['solid', 'outline'],\n description: 'Visual style of the pill.',\n default: 'solid',\n },\n {\n name: 'color',\n type: ['primary', 'critical', 'success', 'warning', 'neutral', 'teal', 'purple', 'pink', 'cyan', 'yellow'],\n description: 'Colour of the pill.',\n default: 'primary',\n },\n ]}\n />\n\n </Fragment>\n\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n <div class=\"grid gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Pill anatomy overview\" src=\"/components/pill/guidelines/figma-01.png\" class=\"md:col-span-2\" />\n <img alt=\"Pill anatomy with icon and text\" src=\"/components/pill/guidelines/figma-02.png\" />\n <img alt=\"Pill anatomy icon-only\" src=\"/components/pill/guidelines/figma-03.png\" />\n </div>\n\n ## Properties\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Color\n\n Pills are available in different semantic colors to support their use case.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Pill color variants\" src=\"/components/pill/guidelines/figma-04.png\" />\n </div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Icon and Label\n\n Pills can have labels with leading or trailing icons, or only an icon.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Pill icon and label variations\" src=\"/components/pill/guidelines/figma-05.png\" />\n </div>\n </div>\n </div>\n\n ## Best Practices\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Spacing\n\n When grouping pills next to each other, always use 8px spacing for consistency.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Pill spacing best practice\" src=\"/components/pill/guidelines/figma-06.png\" />\n </div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Use of Icons\n\n Only use an icon that is universally understood and accessible. Use icons only when necessary and strongly associated with the label text. Avoid decorative-only icons.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Pill icon usage best practice\" src=\"/components/pill/guidelines/figma-07.png\" />\n </div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Pills Are Not Interactive\n\n Pills are for data display only. They are not clickable or focusable and should not be used as triggers for tooltips or popovers.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Pill non-interactive best practice\" src=\"/components/pill/guidelines/figma-08.png\" />\n </div>\n </div>\n </div>\n\n ## Content Guidelines\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Format and Best Practices\n\n - Length: 1-3 words\n - Capitalization: use Title Case\n - If in a series, keep labels all nouns or all verbs when possible\n - Use past tense for completed actions, such as Published\n - Use present tense -ing form for ongoing actions, such as Processing or Loading\n\n As a status message:\n\n - Describe what state a file, content item, or system is in\n - Use terms users already see in the interface\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"Pill content guideline format examples\" src=\"/components/pill/guidelines/figma-09.png\" />\n </div>\n </div>\n </div>\n\n ### Current Examples\n\n - Draft\n - Published / Unpublished\n - Ended\n - Processing / In Process\n - Activated\n - Public\n - Approved, Removed, Hidden for Moderation\n - Added\n - Published On date and time\n - Not Scheduled\n - Scheduled / Scheduled for date and time via Push and Email\n - Not Sent\n - Sent on date and time via Push and Email\n - Creating Preview\n - Preview Ready\n - Importing\n - Completed\n - Original File\n\n ## Usage Examples\n\n <div class=\"grid gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Pill usage example full row\" src=\"/components/pill/guidelines/figma-10.png\" class=\"md:col-span-2\" />\n <img alt=\"Pill usage example detail\" src=\"/components/pill/guidelines/figma-11.png\" class=\"md:col-span-2\" />\n <img alt=\"Pill usage example editorial calendar post flyout\" src=\"/components/pill/guidelines/figma-12.png\" />\n <img alt=\"Pill usage example campaigns\" src=\"/components/pill/guidelines/figma-13.png\" />\n <img alt=\"Pill usage example news channels\" src=\"/components/pill/guidelines/figma-14.png\" />\n <img alt=\"Pill usage example comment moderation\" src=\"/components/pill/guidelines/figma-15.png\" />\n </div>\n\n </Fragment>\n</DocsTabs>"
78
+ },
79
+ {
80
+ "slug": "components/popover",
81
+ "title": "Popover",
82
+ "description": "Popovers display floating content anchored to a trigger element. Use them for non-modal contextual information or actions.",
83
+ "group": "Components",
84
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n <ComponentPreview>\n <PopoverBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Button, InfoIcon, Popover} from '@staffbase/design';\n\n<Popover.Root>\n <Popover.Trigger>\n <Button color=\"neutral\"><InfoIcon />Contributors</Button>\n </Popover.Trigger>\n <Popover.Content className=\"w-[336px]\" side=\"right\">\n <Popover.Close title=\"Close\" />\n <p className=\"text-body-sm mb-1\">\n Contributors can read, publish, and edit their own posts.\n </p>\n <Button variant=\"ghost\" color=\"neutral\">See Support Portal</Button>\n </Popover.Content>\n</Popover.Root>\n```\n </Fragment>\n </ComponentPreview>\n\n ## Anatomy\n\n <Snippet>\n\n```tsx\nimport {Popover} from '@staffbase/design';\n\n<Popover.Root>\n <Popover.Trigger />\n <Popover.Content>\n <Popover.Close />\n </Popover.Content>\n</Popover.Root>;\n```\n\n </Snippet>\n\n ## API reference\n\n ### Root\n\n <BuiltOn url=\"https://www.radix-ui.com/primitives/docs/components/popover#root\">Radix UI Popover Root</BuiltOn>\n\n Groups all parts of the popover and manages its open state.\n\n ### Trigger\n\n <BuiltOn url=\"https://www.radix-ui.com/primitives/docs/components/popover#trigger\">Radix UI Popover Trigger</BuiltOn>\n\n <PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description:\n 'The element that toggles the popover when activated. Passed as a single element, not via a render prop.',\n },\n ]}\n />\n\n ### Content\n\n <BuiltOn url=\"https://www.radix-ui.com/primitives/docs/components/popover#content\">Radix UI Popover Content</BuiltOn>\n\n Renders the floating surface, portalled out of the DOM flow.\n\n <PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The content shown inside the popover.',\n },\n {\n name: 'side',\n type: ['top', 'right', 'bottom', 'left'],\n description: 'Which side of the trigger (or anchor) to position the popover against.',\n default: 'bottom',\n },\n {\n name: 'align',\n type: ['start', 'center', 'end'],\n description: 'The alignment of the popover against the trigger (or anchor).',\n default: 'center',\n },\n {\n name: 'sideOffset',\n type: 'number',\n description: 'The distance in pixels between the popover and its trigger (or anchor).',\n default: '4',\n },\n {\n name: 'container',\n type: ['HTMLElement', 'RefObject<HTMLElement>'],\n description:\n 'The element the popover portal renders into. Use this instead of z-index to control stacking. Defaults to the document body.',\n },\n ]}\n />\n\n ### Close\n\n <BuiltOn url=\"https://www.radix-ui.com/primitives/docs/components/popover#close\">Radix UI Popover Close</BuiltOn>\n\n Renders a close button that dismisses the popover when activated.\n\n <PropTable\n rows={[\n {\n name: 'title',\n type: 'string',\n required: true,\n description: 'An accessible label for the close button, surfaced to assistive technology and as a tooltip.',\n },\n ]}\n />\n\n ### Anchor\n\n <BuiltOn url=\"https://www.radix-ui.com/primitives/docs/components/popover#anchor\">Radix UI Popover Anchor</BuiltOn>\n\n An optional element to position the popover against. When used, the popover is anchored to this element instead of the trigger.\n\n </Fragment>\n\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n <div class=\"grid gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <img alt=\"Popover anatomy overview\" src=\"/components/popover/guidelines/figma-01.png\" class=\"md:col-span-2\" />\n <img alt=\"Popover anatomy with icon trigger\" src=\"/components/popover/guidelines/figma-02.png\" />\n <img alt=\"Popover anatomy with text trigger\" src=\"/components/popover/guidelines/figma-03.png\" />\n </div>\n\n ## Properties\n\n ### Trigger Element\n\n Use a Button as the trigger. Secondary Button and Ghost Button variants are also acceptable.\n\n <div class=\"not-prose\"><img alt=\"Popover property trigger element\" src=\"/components/popover/guidelines/figma-04.png\" /></div>\n\n ### Position\n\n Place the popover where it keeps context with the trigger and remains fully visible in the viewport.\n\n <div class=\"not-prose\"><img alt=\"Popover property position\" src=\"/components/popover/guidelines/figma-05.png\" /></div>\n\n ## Behaviour\n\n ### Interaction\n\n Popovers can be opened by clicking or hovering depending on context, such as information visualization.\n\n If opened via click:\n\n - Close when clicking the trigger again or when clicking outside the popover.\n - Keep the triggering button in a pressed state while the popover is open.\n\n If opened via hover:\n\n - Close when pointer focus leaves both trigger and popover.\n\n <div class=\"not-prose\"><img alt=\"Popover behaviour interaction\" src=\"/components/popover/guidelines/figma-06.png\" /></div>\n\n ## Best Practices\n\n <div class=\"grid items-start gap-x-6 gap-y-8 md:grid-cols-2 not-prose\">\n <div>\n <img alt=\"Popover best practice avoid unnecessary use\" src=\"/components/popover/guidelines/figma-07.png\" />\n <p class=\"mt-3 text-body-sm\"><strong>Don't use Popover when not necessary.</strong> Hiding content can be counterproductive and there are accessible alternatives.</p>\n </div>\n <div>\n <img alt=\"Popover best practice show description text\" src=\"/components/popover/guidelines/figma-08.png\" />\n <p class=\"mt-3 text-body-sm\"><strong>Do: Show content as description text.</strong> It improves clarity and is easier to navigate for assistive technology.</p>\n </div>\n <div>\n <img alt=\"Popover best practice match button height\" src=\"/components/popover/guidelines/figma-09.png\" />\n <p class=\"mt-3 text-body-sm\"><strong>Match button height with line-height.</strong> Align the help button to the text baseline for visual balance.</p>\n </div>\n <div>\n <img alt=\"Popover best practice consistent icons\" src=\"/components/popover/guidelines/figma-10.png\" />\n <p class=\"mt-3 text-body-sm\"><strong>Use icons consistently.</strong> Icon-only help uses question mark, while icon plus text uses info icon.</p>\n </div>\n </div>\n\n ## Usage Examples\n\n ### Help Button\n\n Use to explain advanced contextual information that helps users complete tasks.\n\n <div class=\"not-prose\"><img alt=\"Popover usage example help button\" src=\"/components/popover/guidelines/figma-11.png\" /></div>\n\n ### Custom Content\n\n Popover content can include custom layouts and controls, including buttons.\n\n <div class=\"not-prose\"><img alt=\"Popover usage example custom content\" src=\"/components/popover/guidelines/figma-12.png\" /></div>\n\n ## Content Guidelines\n\n ### Unlabelled Icons or UI Elements\n\n Use a popover when an icon or unlabelled UI element needs clarification.\n\n - Device types\n - Copy, edit, or delete functions\n - Pill labels (see Pill component guidance in this design system)\n\n No heading is needed inside the popover for these short clarifications.\n\n ### Capitalization\n\n If text is 3 words or less, use Title Case with no ending punctuation. For longer sentence content, use sentence case with ending punctuation and keep it brief.\n\n ### Informational Text\n\n Use for chart and graph annotations when users need additional context. Apply sentence case for sentences and keep wording concise. Follow tooltip guidance for similar short explanatory content.\n\n </Fragment>\n</DocsTabs>"
85
+ },
86
+ {
87
+ "slug": "components/searchable-multi-select",
88
+ "title": "Searchable Multi Select",
89
+ "description": "Searchable multi selects let the user filter a list of options by typing and pick several. Selected values appear as chips inside the input.",
90
+ "group": "Components",
91
+ "body": "import {\n SearchableMultiSelectBasic,\n SearchableMultiSelectGrouped,\n SearchableMultiSelectInvalid,\n} from '../../components/demos/SearchableMultiSelectDemo';\n\n<ComponentPreview>\n <SearchableMultiSelectBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, LanguageGlobeIcon, SearchableMultiSelect} from '@staffbase/design';\n\nconst langs = [\n{id: 'js', value: 'JavaScript'},\n{id: 'ts', value: 'TypeScript'},\n{id: 'py', value: 'Python'},\n{id: 'go', value: 'Go'},\n];\n\n<Field.Root className=\"max-w-[400px]\">\n <Field.Label>Programming languages</Field.Label>\n <SearchableMultiSelect.Root items={langs}>\n <SearchableMultiSelect.Value leadingIcon={<LanguageGlobeIcon />}>\n {(value) => (\n <>\n {value.map((language) => (\n <SearchableMultiSelect.Chip key={language.id}>\n {language.value}\n </SearchableMultiSelect.Chip>\n ))}\n <SearchableMultiSelect.Input\n placeholder={value.length > 0 ? '' : 'e.g. TypeScript'}\n />\n </>\n )}\n </SearchableMultiSelect.Value>\n <SearchableMultiSelect.Popup>\n <SearchableMultiSelect.Empty>No languages found.</SearchableMultiSelect.Empty>\n <SearchableMultiSelect.List>\n {(language) => (\n <SearchableMultiSelect.Item key={language.id} value={language}>\n {language.value}\n </SearchableMultiSelect.Item>\n )}\n </SearchableMultiSelect.List>\n </SearchableMultiSelect.Popup>\n </SearchableMultiSelect.Root>\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Field, SearchableMultiSelect} from '@staffbase/design';\n\n<Field.Root>\n <Field.Label />\n <Field.Description />\n <SearchableMultiSelect.Root>\n <SearchableMultiSelect.Value>\n <SearchableMultiSelect.Chip />\n <SearchableMultiSelect.Input />\n </SearchableMultiSelect.Value>\n\n <SearchableMultiSelect.Popup>\n <SearchableMultiSelect.Empty />\n <SearchableMultiSelect.List>\n <SearchableMultiSelect.Group>\n <SearchableMultiSelect.GroupLabel />\n <SearchableMultiSelect.Collection>\n <SearchableMultiSelect.Item />\n </SearchableMultiSelect.Collection>\n </SearchableMultiSelect.Group>\n </SearchableMultiSelect.List>\n </SearchableMultiSelect.Popup>\n\n </SearchableMultiSelect.Root>\n <Field.Error />\n</Field.Root>\n```\n</Snippet>\n\n## API reference\n\nUse the `Field` component to wrap `SearchableMultiSelect` and provide label, description, and error content.\n\nAll parts inherit their base behavior from [Base UI Combobox](https://base-ui.com/react/components/combobox). The sections below document all exports and highlight design-system additions.\n\n### Root\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#root\">Base UI Combobox Root</BuiltOn>\n\nCreates the combobox context and state for filtering and multi-selection.\n\n`SearchableMultiSelect.Root` always enables multi-select behavior (`multiple={true}`).\n\n### Value\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#value\">Base UI Combobox Value</BuiltOn>\n\nRenders the value container, selected chips, clear button, and popup trigger.\n\n<PropTable\n rows={[\n {\n name: 'leadingIcon',\n type: 'ReactNode',\n description: 'Optional icon rendered at the start side of the value container.',\n },\n\n]}\n/>\n\n### Input\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#input\">Base UI Combobox Input</BuiltOn>\n\nText input used to filter available options.\n\n### Popup\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#positioner\">Base UI Combobox Positioner</BuiltOn>\n\nComposed from Portal + Positioner + Popup in one part.\n\n<PropTable\n rows={[\n {\n name: 'container',\n type: ['HTMLElement', 'RefObject<HTMLElement>'],\n description: 'Portal container used to control layering/z-index behavior.',\n },\n ]}\n/>\n\n### List\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#list\">Base UI Combobox List</BuiltOn>\n\nContainer for options, groups, separators, and collection rendering.\n\n### Item\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#item\">Base UI Combobox Item</BuiltOn>\n\nSelectable option row with built-in selected indicator.\n\n### Group\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#group\">Base UI Combobox Group</BuiltOn>\n\nGroups related options together, typically with `GroupLabel` and `Collection`.\n\n### GroupLabel\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#group-label\">Base UI Combobox Group Label</BuiltOn>\n\nLabel for a grouped section of options.\n\n### Separator\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#separator\">Base UI Combobox Separator</BuiltOn>\n\nVisual divider between list sections.\n\n### Chip\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#chip\">Base UI Combobox Chip</BuiltOn>\n\nRepresents a selected value and includes a built-in remove action.\n\n### Empty\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#empty\">Base UI Combobox Empty</BuiltOn>\n\nFallback content shown when no options match the current query.\n\n### Collection\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#collection\">Base UI Combobox Collection</BuiltOn>\n\nHelper part for rendering nested collections (commonly used inside grouped lists).\n\n### Status\n\n<BuiltOn url=\"https://base-ui.com/react/components/combobox#status\">Base UI Combobox Status</BuiltOn>\n\nOptional live-region/status output for communicating result state to assistive technologies.\n\n## Examples\n\n### Grouped\n\n<ComponentPreview>\n <SearchableMultiSelectGrouped client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, LanguageGlobeIcon, SearchableMultiSelect} from '@staffbase/design';\n\nconst groups = [\n{value: 'Fruits', items: ['Apple', 'Banana', 'Orange']},\n{value: 'Vegetables', items: ['Carrot', 'Lettuce', 'Spinach']},\n];\n\n<Field.Root className=\"max-w-[400px]\">\n <Field.Label requiredLabel=\"Required\">Fruits and Vegetables</Field.Label>\n <Field.Description>Select the ones you like</Field.Description>\n <SearchableMultiSelect.Root items={groups}>\n <SearchableMultiSelect.Value leadingIcon={<LanguageGlobeIcon />}>\n {(value: string[]) => (\n <>\n {value.map((item) => (\n <SearchableMultiSelect.Chip key={item}>{item}</SearchableMultiSelect.Chip>\n ))}\n <SearchableMultiSelect.Input placeholder={value.length > 0 ? '' : 'e.g. Apple'} />\n </>\n )}\n </SearchableMultiSelect.Value>\n\n <SearchableMultiSelect.Popup>\n <SearchableMultiSelect.Empty>No produce found</SearchableMultiSelect.Empty>\n <SearchableMultiSelect.List>\n {(group, index) => (\n <>\n <SearchableMultiSelect.Group key={group.value} items={group.items}>\n <SearchableMultiSelect.GroupLabel>{group.value}</SearchableMultiSelect.GroupLabel>\n <SearchableMultiSelect.Collection>\n {(item) => (\n <SearchableMultiSelect.Item key={item.id} value={item}>\n {item}\n </SearchableMultiSelect.Item>\n )}\n </SearchableMultiSelect.Collection>\n </SearchableMultiSelect.Group>\n {index < groups.length - 1 && <SearchableMultiSelect.Separator />}\n </>\n )}\n </SearchableMultiSelect.List>\n </SearchableMultiSelect.Popup>\n\n </SearchableMultiSelect.Root>\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n### With avatars\n\n<ComponentPreview>\n <SearchableMultiSelectInvalid client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Avatar, Field, LanguageGlobeIcon, SearchableMultiSelect} from '@staffbase/design';\n\nconst langs = [\n{id: 'js', value: 'JavaScript', description: 'A popular programming language'},\n{id: 'ts', value: 'TypeScript', description: 'A superset of JavaScript'},\n{id: 'py', value: 'Python', description: 'A versatile programming language'},\n];\n\n<Field.Root invalid className=\"max-w-[400px]\">\n <Field.Label requiredLabel=\"Required\">Programming Languages</Field.Label>\n <Field.Description>Select all that you know</Field.Description>\n <SearchableMultiSelect.Root items={langs}>\n <SearchableMultiSelect.Value leadingIcon={<LanguageGlobeIcon />}>\n {(value) => (\n <>\n {value.map((language) => (\n <SearchableMultiSelect.Chip key={language.id}>{language.value}</SearchableMultiSelect.Chip>\n ))}\n <SearchableMultiSelect.Input placeholder={value.length > 0 ? '' : 'e.g. TypeScript'} />\n </>\n )}\n </SearchableMultiSelect.Value>\n\n <SearchableMultiSelect.Popup>\n <SearchableMultiSelect.Empty>No languages found.</SearchableMultiSelect.Empty>\n <SearchableMultiSelect.List>\n {(language) => (\n <SearchableMultiSelect.Item key={language.id} value={language} className=\"flex items-center gap-2\">\n <Avatar className=\"shrink-0\" />\n <div className=\"space-y-[2px]\">\n <p>{language.value}</p>\n <p className=\"text-body-xs text-neutral-medium\">{language.description}</p>\n </div>\n </SearchableMultiSelect.Item>\n )}\n </SearchableMultiSelect.List>\n </SearchableMultiSelect.Popup>\n\n </SearchableMultiSelect.Root>\n <Field.Error match>Error message</Field.Error>\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>"
92
+ },
93
+ {
94
+ "slug": "components/searchable-single-select",
95
+ "title": "Searchable Single Select",
96
+ "description": "Searchable single selects let the user filter a list of options by typing and pick one. Use them when the list is long enough to benefit from search.",
97
+ "group": "Components",
98
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n <ComponentPreview>\n <SearchableSingleSelectBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, SearchableSingleSelect} from '@staffbase/design';\n\nconst fruits = ['Apple', 'Banana', 'Orange', 'Mango'];\n\n<Field.Root className=\"max-w-[300px]\">\n <Field.Label>Favourite fruit</Field.Label>\n <SearchableSingleSelect.Root items={fruits}>\n <SearchableSingleSelect.Input placeholder=\"e.g. Apple\" />\n <SearchableSingleSelect.Popup>\n <SearchableSingleSelect.Empty>No fruits found.</SearchableSingleSelect.Empty>\n <SearchableSingleSelect.List>\n {(item) => (\n <SearchableSingleSelect.Item key={item} value={item}>\n {item}\n </SearchableSingleSelect.Item>\n )}\n </SearchableSingleSelect.List>\n </SearchableSingleSelect.Popup>\n </SearchableSingleSelect.Root>\n</Field.Root>\n```\n </Fragment>\n </ComponentPreview>\n\n ## Anatomy\n\n <Snippet>\n\n```tsx\nimport {SearchableSingleSelect} from '@staffbase/design';\n\n<SearchableSingleSelect.Root>\n <SearchableSingleSelect.Input />\n <SearchableSingleSelect.Popup>\n <SearchableSingleSelect.Empty />\n <SearchableSingleSelect.List>\n <SearchableSingleSelect.Item />\n </SearchableSingleSelect.List>\n </SearchableSingleSelect.Popup>\n</SearchableSingleSelect.Root>;\n```\n\n </Snippet>\n\n </Fragment>\n\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n <div class=\"flex flex-col gap-8 not-prose\">\n <img alt=\"SearchableSingleSelect anatomy\" src=\"/components/searchable-single-select/guidelines/figma-01.png\" />\n <img alt=\"SearchableSingleSelect anatomy variants\" src=\"/components/searchable-single-select/guidelines/figma-02.png\" />\n </div>\n\n ## Properties\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Searchable\n\n Single Selects can optionally be searchable to find an item within a long list.\n\n When a Single Select is searchable, the caret icon is replaced with a search icon when in the active/open state.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"SearchableSingleSelect searchable property\" src=\"/components/searchable-single-select/guidelines/figma-03.png\" />\n </div>\n </div>\n </div>\n\n\n\n ## Behaviour\n\n <div class=\"flex flex-col gap-8\">\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Dropdown Height\n\n <ul>\n <li>The dropdown in a Single Select can have a maximum height of 272px. If there are fewer dropdown items to reach the maximum height, the dropdown should hug the content.</li>\n </ul>\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"SearchableSingleSelect dropdown height behavior\" src=\"/components/searchable-single-select/guidelines/figma-04.png\" />\n </div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Dropdown Width\n\n <ul>\n <li>The dropdown in a Single Select can have a maximum width of 272px. Otherwise it should hug the content.</li>\n <li>Text should be truncated with an ellipsis when surpassing the maximum width.</li>\n </ul>\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"SearchableSingleSelect dropdown width behavior\" src=\"/components/searchable-single-select/guidelines/figma-05.png\" />\n </div>\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Search Icon\n\n <ul>\n <li>When a Single Select is searchable, the caret icon is replaced with a search icon when in the active/open state.</li>\n </ul>\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"SearchableSingleSelect search icon behavior\" src=\"/components/searchable-single-select/guidelines/figma-06.png\" />\n </div>\n </div>\n <div class=\"[&>h3]:mt-0\">\n\n ### Field Truncation\n\n When in the rest state, Single Selects truncate the value with an ellipsis.<br />When in the active/open state, Single Selects truncate the value without an ellipsis (hard cutoff).\n\n <div class=\"not-prose\"><img alt=\"SearchableSingleSelect field truncation behavior\" src=\"/components/searchable-single-select/guidelines/figma-07.png\" /></div>\n\n </div>\n <div class=\"grid items-start gap-6 md:grid-cols-2\">\n <div class=\"[&>h3]:mt-0\">\n\n ### Dropdown Truncation\n\n Single Select dropdowns truncate items with an ellipsis.\n\n </div>\n <div class=\"not-prose\">\n <img alt=\"SearchableSingleSelect dropdown truncation behavior\" src=\"/components/searchable-single-select/guidelines/figma-08.png\" />\n </div>\n </div>\n </div>\n\n ### Search Override\n\n After opening a Single Select with an existing value, typing clears the current value and begins a new query.\n\n <div class=\"not-prose\"><img alt=\"SearchableSingleSelect search override behavior\" src=\"/components/searchable-single-select/guidelines/figma-09.png\" /></div>\n\n ## Content Guidelines\n\n ### Title\n\n **Capitalization**: Use Title Case.\n\n ### Placeholder\n\n Same as the Search Input component in this Figma file: Use Title Case, then space, followed by an ellipsis: \"Select Option ...\"\n\n ### Description\n\n **Capitalization**: Use sentence case and full sentences followed by a period.\n\n ### Menu Item Label\n\n **Capitalization**: Use Title Case. See the Checkbox component in this Figma file for more content guidelines.\n\n </Fragment>\n</DocsTabs>"
99
+ },
100
+ {
101
+ "slug": "components/segmented-control",
102
+ "title": "Segmented Control",
103
+ "description": "Segmented controls let the user choose one option from a small set of mutually exclusive choices, presented inline.",
104
+ "group": "Components",
105
+ "body": "import {\n SegmentedControlBasic,\n SegmentedControlVariants,\n SegmentedControlWithIcons,\n} from '../../components/demos/SegmentedControlDemo';\n\n<ComponentPreview>\n <SegmentedControlBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {SegmentedControl} from '@staffbase/design';\n\n<SegmentedControl.Root defaultValue=\"list\" aria-label=\"View options\">\n <SegmentedControl.Item value=\"list\">List</SegmentedControl.Item>\n <SegmentedControl.Item value=\"grid\">Grid</SegmentedControl.Item>\n</SegmentedControl.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {SegmentedControl} from '@staffbase/design';\n\n<SegmentedControl.Root>\n <SegmentedControl.Item />\n</SegmentedControl.Root>\n```\n</Snippet>\n\n## Examples\n\n### With icons\n\n<ComponentPreview>\n <SegmentedControlWithIcons client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {BulletListIcon, GridIcon, SegmentedControl} from '@staffbase/design';\n\n<SegmentedControl.Root defaultValue=\"list\" aria-label=\"View options\">\n <SegmentedControl.Item value=\"list\">\n <BulletListIcon />\n List\n </SegmentedControl.Item>\n <SegmentedControl.Item value=\"grid\">\n <GridIcon />\n Grid\n </SegmentedControl.Item>\n</SegmentedControl.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n### Variants\n\n<ComponentPreview>\n <SegmentedControlVariants client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {SegmentedControl} from '@staffbase/design';\n\n<SegmentedControl.Root variant=\"studio\" defaultValue=\"list\" aria-label=\"Studio\">\n <SegmentedControl.Item value=\"list\">List</SegmentedControl.Item>\n <SegmentedControl.Item value=\"grid\">Grid</SegmentedControl.Item>\n</SegmentedControl.Root>\n\n<SegmentedControl.Root variant=\"content\" defaultValue=\"list\" aria-label=\"Content\">\n <SegmentedControl.Item value=\"list\">List</SegmentedControl.Item>\n <SegmentedControl.Item value=\"grid\">Grid</SegmentedControl.Item>\n</SegmentedControl.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## API reference\n\n### Root\n\n<BuiltOn url=\"https://base-ui.com/react/components/radio#radiogroup\">Base UI Radio Group</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'aria-label',\n type: 'string',\n required: true,\n description: 'Accessible label for the segmented control',\n },\n {\n name: 'variant',\n type: ['studio', 'content'],\n description:\n 'Default variant styles are designed for use in studio header. When using Segmented Control in the content area, use the content variant.',\n default: 'studio',\n },\n ]}\n/>\n\n### Item\n\n<BuiltOn url=\"https://base-ui.com/react/components/radio#root\">Base UI Radio Root</BuiltOn>"
106
+ },
107
+ {
108
+ "slug": "components/select",
109
+ "title": "Select",
110
+ "description": "Selects let the user choose a single option from a fixed list. Use them when the available options are known in advance.",
111
+ "group": "Components",
112
+ "body": "<DocsTabs>\n <Fragment slot=\"overview\">\n<ComponentPreview>\n <SelectBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, Select} from '@staffbase/design';\n\nconst fonts = [\n{label: 'Sans-serif', value: 'sans'},\n{label: 'Serif', value: 'serif'},\n{label: 'Monospace', value: 'mono'},\n];\n\n<Field.Root>\n <Field.Label>Font family</Field.Label>\n <Select.Root items={fonts}>\n <Select.Trigger className=\"w-60\">\n <Select.Value />\n </Select.Trigger>\n <Select.Popup>\n <Select.List>\n {fonts.map(({label, value}) => (\n <Select.Item key={value} value={value}>\n {label}\n </Select.Item>\n ))}\n </Select.List>\n </Select.Popup>\n </Select.Root>\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Select} from '@staffbase/design';\n\n<Select.Root>\n <Select.Trigger>\n <Select.Value />\n </Select.Trigger>\n <Select.Popup>\n <Select.List>\n <Select.Item />\n </Select.List>\n </Select.Popup>\n</Select.Root>\n```\n</Snippet>\n\n </Fragment>\n <Fragment slot=\"guidelines\">\n ## Anatomy\n\n Use the anatomy examples to understand how the label, description, error message, leading icon, value, and listbox pieces fit together.\n ![Select anatomy visual example](/components/select/guidelines/figma-01.png)\n\n ![Select anatomy secondary visual example](/components/select/guidelines/figma-02.png)\n\n ![Select anatomy tertiary visual example](/components/select/guidelines/figma-03.png)\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n\n ![Select anatomy compact dropdown example](/components/select/guidelines/figma-04.png)\n\n </div>\n <div>\n\n ![Select anatomy expanded dropdown example](/components/select/guidelines/figma-05.png)\n\n </div>\n </div>\n\n ## Properties\n\n ### Label\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n Single Selects can have a label above the field.\n </p>\n </div>\n <div>\n\n ![Select label example](/components/select/guidelines/figma-06.png)\n\n </div>\n </div>\n\n ### Description Text\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n Use description text below the field when you need to clarify the choice. If an error is present, it replaces the description text.\n </p>\n </div>\n <div>\n\n ![Select description text example](/components/select/guidelines/figma-07.png)\n\n </div>\n </div>\n\n ### Error Message\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n Show an error message below the field when validation fails. The field should also reflect the error state visually.\n </p>\n </div>\n <div>\n\n ![Select error message example](/components/select/guidelines/figma-08.png)\n\n </div>\n </div>\n\n ### Placeholder Text\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n Use placeholder text when no initial value is set. It should suggest the type of option the user can choose.\n </p>\n </div>\n <div>\n\n ![Select placeholder example](/components/select/guidelines/figma-09.png)\n\n </div>\n </div>\n\n ### Leading Icon\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n A leading icon can indicate the content selection type. Use it only when it helps users understand the field more quickly.\n </p>\n </div>\n <div>\n\n ![Select leading icon example](/components/select/guidelines/figma-10.png)\n\n </div>\n </div>\n\n ### Value\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n Use a predefined value when the default choice should be selected on load.\n </p>\n </div>\n <div>\n\n ![Select predefined value example](/components/select/guidelines/figma-11.png)\n\n </div>\n </div>\n\n ### Width\n\n The select can either have a fixed width or fill the available content area.\n ![Select width example](/components/select/guidelines/figma-12.png)\n\n ### IsLoading\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n Use loading state to show a spinner within the dropdown when options are being fetched.\n </p>\n </div>\n <div>\n\n ![Select loading state example](/components/select/guidelines/figma-13.png)\n\n </div>\n </div>\n\n ## Behaviour\n\n ### Interactive States\n\n Hover, press, and focus states should provide the same feedback for keyboard and pointer users.\n ![Select interactive states example](/components/select/guidelines/figma-14.png)\n\n ### Field Min Width\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n Keep the field at least twice its height so it remains legible and stable in layout.\n </p>\n </div>\n <div>\n\n ![Select field min width example](/components/select/guidelines/figma-15.png)\n\n </div>\n </div>\n\n ### Dropdown Height\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n The dropdown can grow to a maximum height of 272px and should hug the content when there are fewer items.\n </p>\n </div>\n <div>\n\n ![Select dropdown height example](/components/select/guidelines/figma-16.png)\n\n </div>\n </div>\n\n ### Dropdown Width\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n Keep the dropdown width compact when possible; text should truncate with an ellipsis if it exceeds the maximum width.\n </p>\n </div>\n <div>\n\n ![Select dropdown width example](/components/select/guidelines/figma-17.png)\n\n </div>\n </div>\n\n ### Caret Flip\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n Flip the caret icon when the dropdown opens so the interaction state is visible.\n </p>\n </div>\n <div>\n\n ![Select caret flip example](/components/select/guidelines/figma-18.png)\n\n </div>\n </div>\n\n ### Scrolling\n\n When item count exceeds the max height, the dropdown becomes scrollable.\n ![Select dropdown scrolling example](/components/select/guidelines/figma-19.png)\n\n ## Best Practices\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n\n ![Select icon usage do example](/components/select/guidelines/figma-20.png)\n\n <p class=\"mt-2\">\n Do: use an icon that reinforces the content type.\n </p>\n </div>\n <div>\n\n ![Select icon usage don't example](/components/select/guidelines/figma-21.png)\n\n <p class=\"mt-2\">\n Don't: use generic decorative icons that do not add meaning.\n </p>\n </div>\n <div>\n\n ![Select inclusive icon example](/components/select/guidelines/figma-22.png)\n\n <p class=\"mt-2\">\n Do: prefer inclusive language icon patterns.\n </p>\n </div>\n <div>\n\n ![Select language flag don't example](/components/select/guidelines/figma-23.png)\n\n <p class=\"mt-2\">\n Don't: use flags to represent languages.\n </p>\n </div>\n </div>\n\n ### Provide a way to make no selection when necessary\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n <p >\n If the use case requires a null value, include an explicit no-selection option in the dropdown.\n </p>\n </div>\n <div>\n\n ![Select no-selection option example](/components/select/guidelines/figma-24.png)\n\n </div>\n </div>\n\n ## Content Guidelines\n\n ### Title\n\n Use Title Case for titles.\n\n ### Placeholder\n\n Use Title Case followed by a space and an ellipsis, such as \"Select Option ...\".\n\n ### Description\n\n Use sentence case with sentences ending in a period.\n\n ### Menu Item Label\n\n Use Title Case for menu item labels.\n\n ## Usage Examples\n\n ### Create News Channel\n\n This example shows a Single Select used in a real flow to choose a space during the Create Channel experience.\n ![Select usage example](/components/select/guidelines/figma-25.png)\n\n ![Select usage compact summary example](/components/select/guidelines/figma-26.png)\n\n <div class=\"grid items-start gap-6 md:grid-cols-2 not-prose [&_img]:md:my-0\">\n <div>\n\n ![Select usage side panel example](/components/select/guidelines/figma-27.png)\n\n </div>\n <div>\n\n ![Select usage dark theme side panel example](/components/select/guidelines/figma-28.png)\n\n </div>\n </div>\n\n ![Select usage metadata example](/components/select/guidelines/figma-29.png)\n\n ![Select usage footer example](/components/select/guidelines/figma-30.png)\n\n </Fragment>\n</DocsTabs>"
113
+ },
114
+ {
115
+ "slug": "components/separator",
116
+ "title": "Separator",
117
+ "description": "Separators visually divide groups of content using a thin horizontal or vertical line.",
118
+ "group": "Components",
119
+ "body": "<ComponentPreview>\n <div className='space-y-2'>\n <p>Top content</p>\n <Separator orientation=\"horizontal\" />\n <p>Bottom content</p>\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {Separator} from '@staffbase/design';\n\n<div>\n <p>Top content</p>\n <Separator orientation=\"horizontal\" />\n <p>Bottom content</p>\n</div>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Separator} from '@staffbase/design';\n\n<Separator />\n```\n</Snippet>\n\n## Examples\n\n### Vertical\n\n<ComponentPreview>\n <div class=\"flex gap-2\">\n <span>Left</span>\n <Separator orientation=\"vertical\" />\n <span>Right</span>\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {Separator} from '@staffbase/design';\n\n<div class=\"flex gap-2\">\n <span>Left</span>\n <Separator orientation=\"vertical\" />\n <span>Right</span>\n</div>\n```\n </Fragment>\n</ComponentPreview>\n\n## API reference\n\n### Separator\n\n<PropTable\n rows={[\n {\n name: 'orientation',\n type: ['horizontal', 'vertical'],\n description: 'The axis along which the separator is rendered.',\n default: 'horizontal',\n },\n ]}\n/>"
120
+ },
121
+ {
122
+ "slug": "components/skeleton",
123
+ "title": "Skeleton",
124
+ "description": "Skeletons indicate loading content using placeholder shapes. They reduce perceived wait time while data loads.",
125
+ "group": "Components",
126
+ "body": "<ComponentPreview>\n <div className=\"flex w-full gap-3\">\n <Skeleton.Avatar size={48} />\n <div className=\"flex-1\">\n <Skeleton.Text lines={3} />\n </div>\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {Skeleton} from '@staffbase/design';\n\n<div className=\"flex gap-3\">\n <Skeleton.Avatar size={48} />\n <div className=\"flex-1\">\n <Skeleton.Text lines={3} />\n </div>\n</div>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Skeleton} from '@staffbase/design';\n\n<Skeleton.Avatar />\n<Skeleton.Text />\n<Skeleton.Box />\n```\n</Snippet>\n\n## Examples\n\n## Text\n\n<ComponentPreview>\n <div className=\"w-full\">\n <Skeleton.Text className=\"mb-3 max-w-[30%]\" />\n <Skeleton.Text lines={3} />\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {Skeleton} from '@staffbase/design';\n\n<Skeleton.Text className=\"mb-3 max-w-[30%]\" />\n<Skeleton.Text lines={3} />\n```\n </Fragment>\n</ComponentPreview>\n\n### Box\n\n<ComponentPreview>\n <div className=\"w-full space-y-2\">\n <Skeleton.Box className=\"aspect-4/3 h-20\" />\n <Skeleton.Box className=\"size-16\" />\n <Skeleton.Box className=\"h-[40px] w-[400px]\" />\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {Skeleton} from '@staffbase/design';\n\n<Skeleton.Box className=\"aspect-4/3 h-20\" />\n<Skeleton.Box className=\"size-16\" />\n<Skeleton.Box className=\"h-[40px] w-[400px]\" />\n```\n </Fragment>\n</ComponentPreview>"
127
+ },
128
+ {
129
+ "slug": "components/style-chip",
130
+ "title": "Style Chip",
131
+ "description": "Style chips display a short label, often used to indicate file types or formats.",
132
+ "group": "Components",
133
+ "body": "<ComponentPreview>\n <div className='flex gap-2'>\n <StyleChip>PNG</StyleChip>\n <StyleChip>JPG</StyleChip>\n <StyleChip>PDF</StyleChip>\n <StyleChip>SVG</StyleChip>\n </div>\n <Fragment slot=\"code\">\n```tsx\nimport {StyleChip} from '@staffbase/design';\n\n<StyleChip>PNG</StyleChip>\n<StyleChip>JPG</StyleChip>\n<StyleChip>PDF</StyleChip>\n<StyleChip>SVG</StyleChip>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {StyleChip} from '@staffbase/design';\n\n<StyleChip />\n```\n</Snippet>\n\n## API reference\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n description: 'Content displayed on StyleChip',\n },\n ]}\n/>"
134
+ },
135
+ {
136
+ "slug": "components/switch",
137
+ "title": "Switch",
138
+ "description": "Switches toggle a single setting on or off. The change takes effect immediately.",
139
+ "group": "Components",
140
+ "body": "<ComponentPreview>\n <SwitchBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, Switch} from '@staffbase/design';\n\n<Field.Root className=\"flex gap-2\">\n <Switch />\n <div>\n <Field.Label>Enable notifications</Field.Label>\n <Field.Description>Receive updates via email.</Field.Description>\n </div>\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Switch} from '@staffbase/design';\n\n<Switch />\n```\n</Snippet>\n\n## API reference\n\n### Switch\n\n<BuiltOn url=\"https://base-ui.com/react/components/switch#root\">Base UI Switch Root</BuiltOn>\n\nRenders a toggle that switches a single setting on or off. Pair it with `Field` to associate a label and description.\n\n<PropTable\n rows={[\n {\n name: 'checked',\n type: 'boolean',\n description: 'The controlled checked state of the switch. Use together with onCheckedChange.',\n },\n {\n name: 'defaultChecked',\n type: 'boolean',\n description:\n 'The checked state of the switch when it is initially rendered. Use when you do not need to control its state.',\n default: 'false',\n },\n {\n name: 'onCheckedChange',\n type: '(checked: boolean, event: Event) => void',\n description: 'Callback fired when the checked state changes.',\n },\n {\n name: 'disabled',\n type: 'boolean',\n description: 'Whether the switch should ignore user interaction.',\n default: 'false',\n },\n {\n name: 'readOnly',\n type: 'boolean',\n description: 'Whether the user should be unable to change the checked state.',\n default: 'false',\n },\n {\n name: 'required',\n type: 'boolean',\n description: 'Whether the switch must be checked before an owning form can be submitted.',\n default: 'false',\n },\n {\n name: 'name',\n type: 'string',\n description: 'Identifies the switch when a form is submitted.',\n },\n ]}\n/>"
141
+ },
142
+ {
143
+ "slug": "components/tabs",
144
+ "title": "Tabs",
145
+ "description": "Tabs organise content into separate views, allowing the user to switch between them.",
146
+ "group": "Components",
147
+ "body": "<ComponentPreview>\n <TabsBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Tabs} from '@staffbase/design';\n\n<Tabs.Root defaultValue=\"tab1\">\n <Tabs.List className=\"mb-4\">\n <Tabs.Trigger value=\"tab1\">Tab Item 1</Tabs.Trigger>\n <Tabs.Trigger value=\"tab2\">Tab Item 2</Tabs.Trigger>\n <Tabs.Trigger value=\"tab3\">Tab Item 3</Tabs.Trigger>\n </Tabs.List>\n <Tabs.Content value=\"tab1\">Tab Content 1</Tabs.Content>\n <Tabs.Content value=\"tab2\">Tab Content 2</Tabs.Content>\n <Tabs.Content value=\"tab3\">Tab Content 3</Tabs.Content>\n</Tabs.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Tabs} from '@staffbase/design';\n\n<Tabs.Root>\n <Tabs.List>\n <Tabs.Trigger />\n </Tabs.List>\n <Tabs.Content />\n</Tabs.Root>\n```\n</Snippet>\n\n## API reference\n\n### Root\n\n<BuiltOn url=\"https://www.radix-ui.com/primitives/docs/components/tabs#root\">Radix UI Tabs Root</BuiltOn>\n\nGroups all parts of the tabs and manages which tab is selected.\n\n<PropTable\n rows={[\n {\n name: 'value',\n type: 'string',\n description: 'The value of the selected tab when controlled. Use together with onValueChange.',\n },\n {\n name: 'defaultValue',\n type: 'string',\n description:\n 'The value of the tab selected when initially rendered. Use when you do not need to control the selected tab.',\n },\n {\n name: 'onValueChange',\n type: '(value: string) => void',\n description: 'Callback fired when the selected tab changes.',\n },\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The tab list and content panels.',\n },\n\n]}\n/>\n\n### List\n\n<BuiltOn url=\"https://www.radix-ui.com/primitives/docs/components/tabs#list\">Radix UI Tabs List</BuiltOn>\n\nRenders the container that lays out the tab triggers.\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The tab triggers.',\n },\n ]}\n/>\n\n### Trigger\n\n<BuiltOn url=\"https://www.radix-ui.com/primitives/docs/components/tabs#trigger\">Radix UI Tabs Trigger</BuiltOn>\n\nRenders the button that activates its associated content panel.\n\n<PropTable\n rows={[\n {\n name: 'value',\n type: 'string',\n required: true,\n description: 'A unique value that associates the trigger with a content panel.',\n },\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The label shown inside the tab.',\n },\n ]}\n/>\n\n### Content\n\n<BuiltOn url=\"https://www.radix-ui.com/primitives/docs/components/tabs#content\">Radix UI Tabs Content</BuiltOn>\n\nRenders the panel shown when its associated trigger is selected.\n\n<PropTable\n rows={[\n {\n name: 'value',\n type: 'string',\n required: true,\n description: 'A unique value that associates the panel with its trigger.',\n },\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The content shown when the tab is selected.',\n },\n\n]}\n/>\n\n## Examples\n\n### With badges\n\n<ComponentPreview>\n <TabsWithBadges client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Badge, Tabs} from '@staffbase/design';\n\n<Tabs.Root defaultValue=\"inbox\">\n <Tabs.List className=\"mb-4\">\n <Tabs.Trigger value=\"inbox\">Inbox</Tabs.Trigger>\n <Tabs.Trigger value=\"drafts\">\n Drafts\n <Badge a11yDescription=\"Notification\" value={4} variant=\"primary\" />\n </Tabs.Trigger>\n <Tabs.Trigger value=\"alerts\">\n Alerts\n <Badge a11yDescription=\"Notification\" value={99} variant=\"critical\" />\n </Tabs.Trigger>\n </Tabs.List>\n <Tabs.Content value=\"inbox\">Inbox content</Tabs.Content>\n <Tabs.Content value=\"drafts\">Drafts content</Tabs.Content>\n <Tabs.Content value=\"alerts\">Alerts content</Tabs.Content>\n</Tabs.Root>\n```\n </Fragment>\n</ComponentPreview>"
148
+ },
149
+ {
150
+ "slug": "components/text-area",
151
+ "title": "Text Area",
152
+ "description": "Text areas allow the user to enter multi-line text, such as comments or descriptions.",
153
+ "group": "Components",
154
+ "body": "<ComponentPreview>\n <TextAreaBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, TextArea} from '@staffbase/design';\n\n<Field.Root className=\"w-[400px]\">\n <Field.Label>Description</Field.Label>\n <Field.Description>Enter a brief description.</Field.Description>\n <TextArea rows={4} />\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {TextArea} from '@staffbase/design';\n\n<TextArea />\n```\n</Snippet>\n\n## Examples\n\n### Invalid\n\n<ComponentPreview>\n <TextAreaInvalid client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, TextArea} from '@staffbase/design';\n\n<Field.Root invalid className=\"w-[400px]\">\n <Field.Label>Description</Field.Label>\n <TextArea />\n <Field.Error match>This field is required.</Field.Error>\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## API reference\n\n### TextArea\n\n<BuiltOn url=\"https://base-ui.com/react/components/input#root\">Base UI Input Root</BuiltOn>\n\nRenders a multi-line text input. Combine with `Field` to provide accessible labels, descriptions, and errors.\n\nSupports all Base UI Input props and native textarea attributes. The table below highlights commonly used props.\n\n<PropTable\n rows={[\n {\n name: 'rows',\n type: 'number',\n description: 'The number of visible text lines.',\n },\n ]}\n/>"
155
+ },
156
+ {
157
+ "slug": "components/text-field",
158
+ "title": "Text Field",
159
+ "description": "Text fields let the user enter a single line of text, such as a name or email address.",
160
+ "group": "Components",
161
+ "body": "<ComponentPreview>\n <TextFieldBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, TextField} from '@staffbase/design';\n\n<Field.Root className=\"max-w-[300px]\">\n <Field.Label>Name</Field.Label>\n <Field.Description>Enter your full name.</Field.Description>\n <TextField />\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {TextField} from '@staffbase/design';\n\n<TextField />\n```\n</Snippet>\n\n## Examples\n\n### With icons\n\n<ComponentPreview>\n <TextFieldWithIcons client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, HomeIcon, TextField} from '@staffbase/design';\n\n<Field.Root className=\"max-w-[300px]\">\n <Field.Label>Address</Field.Label>\n <TextField leadingIcon={<HomeIcon />} />\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n### Invalid\n\n<ComponentPreview>\n <TextFieldInvalid client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Field, TextField} from '@staffbase/design';\n\n<Field.Root invalid className=\"max-w-[300px]\">\n <Field.Label>Email</Field.Label>\n <TextField />\n <Field.Error match>Enter a valid email address.</Field.Error>\n</Field.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## API reference\n\n### TextField\n\n<BuiltOn url=\"https://base-ui.com/react/components/input#root\">Base UI Input Root</BuiltOn>\n\nRenders a single-line text input. Combine with `Field` to provide accessible labels, descriptions, and errors.\n\nSupports all Base UI Input props and native input attributes. The table below highlights TextField-specific and constrained props.\n\n<PropTable\n rows={[\n {\n name: 'type',\n type: ['text', 'email', 'password', 'tel', 'url'],\n description: 'The HTML input type rendered by the field.',\n default: 'text',\n },\n {\n name: 'leadingIcon',\n type: 'ReactNode',\n description: 'An icon rendered at the start side of the input.',\n },\n {\n name: 'trailingIcon',\n type: 'ReactNode',\n description: 'An icon rendered at the end side of the input.',\n },\n ]}\n/>"
162
+ },
163
+ {
164
+ "slug": "components/tooltip",
165
+ "title": "Tooltip",
166
+ "description": "Tooltips display a short label or description when the user hovers or focuses on an element.",
167
+ "group": "Components",
168
+ "body": "<ComponentPreview>\n <TooltipLabel client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Button, SettingsIcon, Tooltip} from '@staffbase/design';\n\n<Tooltip.Root>\n <Tooltip.Trigger>\n <Button variant=\"ghost\" color=\"neutral\" iconOnly aria-label=\"Settings\"><SettingsIcon /></Button>\n </Tooltip.Trigger>\n <Tooltip.Content is=\"label\" side=\"bottom\">\n Settings\n </Tooltip.Content>\n</Tooltip.Root>\n```\n </Fragment>\n</ComponentPreview>\n\n## Anatomy\n\n<Snippet>\n```tsx\nimport {Tooltip} from '@staffbase/design';\n\n<Tooltip.Root>\n <Tooltip.Trigger />\n <Tooltip.Content />\n</Tooltip.Root>\n```\n</Snippet>\n\n## API reference\n\n### Root\n\n<BuiltOn url=\"https://base-ui.com/react/components/tooltip#root\">Base UI Tooltip Root</BuiltOn>\n\nWraps the tooltip and manages its open state. The provider is included internally, so you don't need to render `Tooltip.Provider` yourself.\n\n### Trigger\n\n<BuiltOn url=\"https://base-ui.com/react/components/tooltip#trigger\">Base UI Tooltip Trigger</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description:\n 'The element that reveals the tooltip on hover or focus. Passed as a single element, not via a render prop.',\n },\n ]}\n/>\n\n### Content\n\n<BuiltOn url=\"https://base-ui.com/react/components/tooltip#positioner\">Base UI Tooltip Positioner</BuiltOn>\n\n<PropTable\n rows={[\n {\n name: 'is',\n type: ['label', 'description', 'visual-only'],\n required: true,\n description:\n 'How the tooltip relates to its trigger for assistive technology. Use \"label\" when the tooltip names the trigger (e.g. an icon button), \"description\" when it adds supplementary detail, or \"visual-only\" when the content is already conveyed elsewhere.',\n default: 'description',\n },\n {\n name: 'variant',\n type: ['default', 'inverse'],\n description: 'The visual style of the tooltip surface.',\n default: 'default',\n },\n {\n name: 'side',\n type: ['top', 'bottom', 'left', 'right', 'inline-start', 'inline-end'],\n description: 'Which side of the trigger to position the tooltip against.',\n default: 'top',\n },\n {\n name: 'container',\n type: ['HTMLElement', 'RefObject<HTMLElement>'],\n description:\n 'The element the tooltip portal renders into. Use this instead of z-index to control stacking. Defaults to the document body.',\n },\n {\n name: 'children',\n type: 'ReactNode',\n required: true,\n description: 'The content shown inside the tooltip.',\n },\n ]}\n/>\n\n## Examples\n\n### As a description\n\n<ComponentPreview>\n <TooltipDescription client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {Tooltip} from '@staffbase/design';\n\n<Tooltip.Root>\n <Tooltip.Trigger>\n <a href=\"#\" className=\"underline\">More information</a>\n </Tooltip.Trigger>\n <Tooltip.Content is=\"description\" side=\"right\">\n Additional context shown when hovering this link.\n </Tooltip.Content>\n</Tooltip.Root>\n```\n </Fragment>\n</ComponentPreview>"
169
+ },
170
+ {
171
+ "slug": "foundation/tokens/colors",
172
+ "title": "Color tokens",
173
+ "group": "Foundation",
174
+ "body": "import {\n primitiveColorGroups,\n semanticChartColorRows,\n semanticTextColorRows,\n semanticIconColorRows,\n semanticBorderColorRows,\n semanticBackgroundColorRows,\n} from '../../../components/tokens/tokenData';\n\n<TokenSearch client:idle placeholder=\"Search color tokens…\" label=\"Search color tokens\" />\n\n## Semantic\n\n### Text\n\n<TokenTable rows={semanticTextColorRows} />\n\n### Icon\n\n<TokenTable rows={semanticIconColorRows} />\n\n### Border\n\n<TokenTable rows={semanticBorderColorRows} />\n\n### Background\n\n<TokenTable rows={semanticBackgroundColorRows} />\n\n### Chart\n\n<TokenTable rows={semanticChartColorRows} />\n\n## Primitive\n\n<Banner variant=\"warning\" size=\"md\" className=\"mb-4\">\n <strong>Do not use primitive color tokens in production code.</strong> Referencing a primitive color directly (e.g.{' '}\n <code>--sb-color-blue-600</code>) hard-codes a specific value which breaks theming. Always use semantic tokens (e.g.{' '}\n <code>border-primary</code>) so your UI responds to theme changes. Primitive tokens are listed here for reference\n only.\n</Banner>\n\n{Object.entries(primitiveColorGroups).map(([name, rows]) => (\n\n <div key={name}>\n <h3 className='text-title-md'>{name}</h3>\n <TokenTable rows={rows} />\n </div>\n))}"
175
+ },
176
+ {
177
+ "slug": "foundation/tokens/component",
178
+ "title": "Component tokens",
179
+ "group": "Foundation",
180
+ "body": "Component tokens are design decisions scoped to a specific component. They sit above semantic tokens in the hierarchy, allowing per-component overrides without touching shared values.\n\n## Badge\n\n<TokenTable rows={componentBadgeRows} hideValue />\n\n## Button\n\n<TokenTable rows={componentButtonRows} hideValue />\n\n## Link\n\n<TokenTable rows={componentLinkRows} hideValue />"
181
+ },
182
+ {
183
+ "slug": "foundation/tokens/radius",
184
+ "title": "Radius tokens",
185
+ "group": "Foundation",
186
+ "body": "<TokenTable rows={primitiveRadiusRows} />"
187
+ },
188
+ {
189
+ "slug": "foundation/tokens/shadow",
190
+ "title": "Shadow tokens",
191
+ "group": "Foundation",
192
+ "body": "<TokenTable rows={primitiveShadowRows} />"
193
+ },
194
+ {
195
+ "slug": "foundation/tokens/typography",
196
+ "title": "Typography tokens",
197
+ "group": "Foundation",
198
+ "body": "import {\n primitiveFontFamilyRows,\n primitiveFontSizeRows,\n primitiveFontWeightRows,\n primitiveLineHeightRows,\n primitiveLetterSpacingRows,\n typographyPresetRows,\n} from '../../../components/tokens/tokenData';\n\n## Typography presets\n\n<TypographyTable rows={typographyPresetRows} />\n\n## Font family\n\n<TokenTable rows={primitiveFontFamilyRows} />\n\n## Font size\n\n<TokenTable rows={primitiveFontSizeRows} />\n\n## Font weight\n\n<TokenTable rows={primitiveFontWeightRows} />\n\n## Line height\n\n<TokenTable rows={primitiveLineHeightRows} />\n\n## Letter spacing\n\n<TokenTable rows={primitiveLetterSpacingRows} />"
199
+ },
200
+ {
201
+ "slug": "guides/contribution",
202
+ "title": "Contribution",
203
+ "group": "Other",
204
+ "body": "As we're in a ramp up phase of our design system we want to clarify how contributions work for now.\n\n## I found a bug 🐞\n\nIf you've found a bug and the fix is small and contained, go ahead and open a PR. These are usually quick to review and merge, and they help us move fast together.\n\nIf you've found a bug but aren't sure what the right fix is, or you don't have capacity to work on it yourself, please [open a GitHub issue](https://github.com/staffbase/design/issues) instead. A rough write-up is totally fine β€” we'll pick it up from there.\n\n## I want to request a new component or pattern\n\nPlease [open a GitHub issue](https://github.com/staffbase/design/issues) to request it. Tracking needs as issues helps us:\n\n- spot recurring themes\n- identify priorities\n- keep context and decisions in one place over time\n\nBigger changes like new components or patterns need:\n\n- alignment with system standards\n- accessibility review\n- design review\n- API and long-term maintenance consideration\n- coordination across teams\n\nSupporting this level of work requires more hands-on involvement than we can realistically offer right now. In many cases, it's more effective for us to own and build these changes ourselves, with input from you along the way β€” so an issue is the best place to start the conversation.\n\n## I want to make a small improvement\n\nBecause we're currently a small team, we're happily accepting small, contained PRs β€” for example:\n\n- documentation\n- small visual or a11y improvements\n- [adding an icon](#i-want-to-add-an-icon)\n\nThese are usually quick to review and merge.\n\n## I want to add an icon\n\n1. Obtain the icon file in SVG format from your designer.\n\n2. Place the SVG in the `icons_raw` directory. Use kebab-case for SVG filenames.\n\n If the icon represents a toggleable or secondary state (e.g. locked/unlocked, filled/unfilled), add both versions. Use `-alt` in the filename to indicate the alternate or toggled version β€” e.g. `bookmark.svg` and `bookmark-alt.svg`.\n\n3. Run `pnpm generate:icons` to process the raw SVG and generate the Icon component.\n\n `index.tsx` and `iconList.tsx` in `src/icons` will also be updated.\n\n4. Start Astro locally to preview the new icon.\n\n Confirm that the icon appears and looks as expected. If needed, you can fine-tune the generated icon component directly in `src/icons`.\n\n5. Add search keywords for the icon (recommended).\n\n The icon gallery search is powered by `apps/astro/src/components/icons/iconTags.ts`. Add an entry for your icon with related terms so people can find it by synonym β€” e.g. `bin: ['delete', 'trash', 'remove']`. Without it the icon still appears in the gallery, but is only findable by its exact name.\n\n6. Run `pnpm changeset` to add a changeset β€” a new icon is a new public export and is user-visible, so it requires one.\n\n## I want to change tokens\n\n### Token layers\n\nToken sources live in four JSON files under `packages/design/tokens/`:\n\n- **`primitive.json`** β€” raw values (colors, font sizes, radii, shadows). These are the lowest-level building blocks. You'll rarely edit these unless adding a new color scale or adjusting a base value.\n- **`semantic.json`** β€” reference primitive tokens. Contextual aliases that adapt to theming.\n- **`component.json`** β€” per-component overrides for when semantic tokens aren't specific enough (e.g. `badge.text.color.critical`). Only add here if a component needs a value that doesn't map cleanly to an existing semantic token.\n- **`typography-presets.json`** β€” composite typography tokens that bundle `fontSize`, `lineHeight`, and `fontWeight` into named presets (e.g. `body.lg`, `title.md`, `label.sm`).\n\nAll JSON files use the [DTCG (Design Tokens Community Group)](https://tr.designtokens.org/format/) format:\n\n<Snippet label=\"primitive.json\">\n```json\n\"blue\": {\n \"500\": {\n \"$value\": \"oklch(0.6388 0.1952 257.61)\"\n }\n}\n```\n</Snippet>\n\n<Snippet label=\"semantic.json\">\n```json\n\"text\": {\n \"color\": {\n \"primary\": {\n \"$value\": \"{color.blue.500}\"\n }\n }\n}\n```\n</Snippet>\n\n### How it works\n\nRunning `pnpm --filter @staffbase/design generate:tokens` executes two scripts in sequence:\n\n1. **`tokens/sd.config.ts`** β€” runs [Style Dictionary](https://styledictionary.com/) to convert the JSON tokens into CSS variables (one CSS file per layer).\n2. **`scripts/generateTheme.ts`** β€” generates a Tailwind v4 theme file (`@theme inline`) that bridges `--sb-*` custom properties to Tailwind utility namespaces. It also maps typography presets into Tailwind's `--text-*` namespace.\n\nThis produces the following generated files (do not edit these directly):\n\n- `src/foundation/tokens/primitive.css`\n- `src/foundation/tokens/semantic.css`\n- `src/foundation/tokens/component.css`\n- `src/foundation/theme.css`\n\n### Workflow\n\n1. Edit the appropriate JSON source file in `packages/design/tokens/`.\n\n2. Run `pnpm --filter @staffbase/design generate:tokens` to regenerate the CSS output.\n3. Start the docs `pnpm dev:astro` locally to verify affected components still look correct.\n\n4. Run `pnpm changeset` to add a changeset β€” token changes are always user-visible and require one."
205
+ },
206
+ {
207
+ "slug": "guides/setup",
208
+ "title": "Setup",
209
+ "group": "Other",
210
+ "body": "How to quickly start using the Staffbase Design System within projects, for engineers.\n\n## Overview\n\nThe Staffbase Design System brings order and consistency to all Staffbase products through a single package of UI components, icons, and design tokens.\n\n- Available as a private npm package found at `@staffbase/design`.\n- Built on **React 18/19**\n- For styling it uses **Tailwind v4**\n- Most compound components are built using Base UI primitives.\n\n## Requirements\n\nBefore you start, make sure your project has:\n\n- **React 18 or 19** (`react` and `react-dom`) β€” these are peer dependencies and aren't bundled.\n- **Tailwind v4** set up in your project. Follow the [installation instructions from Tailwind](https://tailwindcss.com/docs/installation).\n\n## Installation\n\nSince this is a private npm package you will first need to [set up your npm config](https://backstage.staffbase.com/docs/default/component/sondoku/client/npm-userconfig/) so your package manager can authenticate against the registry.\n\nThen install the design system:\n\n{/* prettier-ignore */}\n<Snippet label=\"Terminal\">\n```bash\npnpm install @staffbase/design\n```\n</Snippet>\n\n## Configuration\n\nImport the following into your application's global stylesheet. The cascade layer order is declared up front so that rules in later layers override earlier ones β€” keep this order intact.\n\n<Snippet label=\"CSS\">\n```css\n@layer theme, base, components, utilities;\n\n@import 'tailwindcss/theme.css' layer(theme);\n@import 'tailwindcss/preflight.css' layer(base);\n@import 'tailwindcss/utilities.css' layer(utilities);\n\n@import '@staffbase/design/fonts/inter.css' layer(base);\n@import '@staffbase/design/fonts/epilogue.css' layer(base);\n@import '@staffbase/design/tokens/primitive.css' layer(base);\n@import '@staffbase/design/tokens/semantic.css' layer(base);\n@import '@staffbase/design/tokens/component.css' layer(base);\n\n@import '@staffbase/design/components.css' layer(components);\n\n@import '@staffbase/design/theme.css' layer(theme);\n\n````\n</Snippet>\n\nWhat each import does:\n\n- **Tailwind layers** β€” the framework's theme, preflight reset, and utilities.\n- **Fonts** β€” `inter.css` and `epilogue.css` register the Staffbase typefaces (see [Fonts](#fonts) below).\n- **Token layers** β€” `primitive`, `semantic`, and `component` expose the design tokens as CSS custom properties. Prefer semantic tokens in your own code; they adapt to dark mode and theming.\n- **`components.css`** β€” the bundled component styles. Always import this single file; never import per-component CSS.\n- **`theme.css`** β€” the Tailwind v4 theme bridge that maps tokens onto Tailwind utilities.\n\nYou can now use Tailwind classes in your markup πŸŽ‰\n\n{/* prettier-ignore */}\n<Snippet label=\"HTML\">\n```html\n<div class=\"text-neutral-strong p-1\">Hello World</div>\n````\n\n</Snippet>\n\n<Banner variant=\"warning\" size=\"md\">\n Don't use `@apply` for your CSS. We use `@apply` in the Design System itself but this will soon be removed. See\n [tailwind docs](https://tailwindcss.com/docs/reusing-styles#avoiding-premature-abstraction) for more information.\n</Banner>\n\n## Fonts\n\nThe design system ships with copies of the Staffbase typefaces, Inter and Epilogue. These are registered by the `@staffbase/design/fonts/inter.css` and `@staffbase/design/fonts/epilogue.css` imports in the CSS block above, so you don't need to preload or serve the font files separately.\n\n## CSS reset\n\nTailwind's `preflight.css` (imported above) acts as the CSS reset, normalizing native browser styling to avoid conflicts with component styles. If your project already ships its own reset, keep the cascade layer order intact so design system styles win where expected.\n\n## Usage\n\nImport the component you need directly from the package:\n\n{/* prettier-ignore */}\n<Snippet label=\"TSX\">\n```tsx\nimport {Button} from '@staffbase/design';\n```\n</Snippet>\n\nAnd use it as you would any React component:\n\n{/* prettier-ignore */}\n<Snippet label=\"TSX\">\n```tsx\n<Button>Hello World</Button>\n```\n</Snippet>\n\n### Icons\n\nIcon components are exported from the same entry point as components:\n\n{/* prettier-ignore */}\n<Snippet label=\"TSX\">\n```tsx\nimport {CheckIcon} from '@staffbase/design';\n```\n</Snippet>\n\n### Hooks\n\nHooks live behind a separate entry point:\n\n{/* prettier-ignore */}\n<Snippet label=\"TSX\">\n```tsx\nimport {useFormatDateTime} from '@staffbase/design/hooks';\n```\n</Snippet>\n\n## Editor support\n\n### VS Code\n\nIf you're using VS Code, we recommend installing the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension. This provides code completion for Tailwind classes.\n\n### IntelliJ IDEA\n\nIf you're using IntelliJ IDEA, there's nothing you need to do. The IDE will automatically pick up the Tailwind classes and provide code completion.\n\n```\n\n```"
211
+ },
212
+ {
213
+ "slug": "hooks/use-format-date-time",
214
+ "title": "useFormatDateTime",
215
+ "description": "A React hook that returns a memoised Intl.DateTimeFormat instance, letting you format dates and times in any locale with just a few props.",
216
+ "group": "Other",
217
+ "body": "import {\n DateTimeBasic,\n DateStyleTable,\n TimeStyleTable,\n CombinedTable,\n DateRangeDemo,\n} from '../../components/demos/UseFormatDateTimeDemo';\n\n<ComponentPreview>\n <DateTimeBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {useFormatDateTime} from '@staffbase/design/hooks';\n\nconst formatter = useFormatDateTime('en-US', 'long', 'short');\n\nformatter.format(new Date(2025, 11, 25, 14, 30)); // \"December 25, 2025 at 2:30 PM\"\n\n````\n </Fragment>\n</ComponentPreview>\n\nThe formatter is only recreated when `locale`, `dateStyle`, or `timeStyle` actually change β€” it's memoised with `useMemo` internally, so the returned object is stable between renders.\n\n## Usage\n\nImport the hook from the package and call it with a locale and the desired format styles.\n\n<Snippet>\n```tsx\nimport {useFormatDateTime} from '@staffbase/design/hooks';\n\nconst EventDate = ({date}: {date: Date}) => {\n const locale = navigator.language; // or from your i18n context\n const formatter = useFormatDateTime(locale, 'long', 'short');\n\n return <time dateTime={date.toISOString()}>{formatter.format(date)}</time>;\n};\n````\n\n</Snippet>\n\n### Date only vs time only\n\nPass `undefined` for `timeStyle` to get a date-only output, or `undefined` for `dateStyle` to get a time-only output.\n\n<Snippet>\n```tsx\n// Date only β€” pass undefined for timeStyle\nconst dateFormatter = useFormatDateTime('de-DE', 'long', undefined);\ndateFormatter.format(new Date()); // \"25. Dezember 2025\"\n\n// Time only β€” pass undefined for dateStyle\nconst timeFormatter = useFormatDateTime('de-DE', undefined, 'short');\ntimeFormatter.format(new Date()); // \"14:30\"\n\n````\n</Snippet>\n\n## Format styles\n\nThe same reference date β€” **December 25, 2025 at 14:30** β€” rendered across every format option. Switch the language dropdown above each table to see how the output changes per locale.\n\n### dateStyle\n\n`dateStyle` controls how the date portion is rendered. Pass `undefined` for `timeStyle` to get a date-only output.\n\n<DateStyleTable client:load />\n\n### timeStyle\n\n`timeStyle` controls how the time portion is rendered. Pass `undefined` for `dateStyle` to get a time-only output.\n\n<TimeStyleTable client:load />\n\n### Combined\n\nWhen both `dateStyle` and `timeStyle` are provided, the date and time are joined in a single locale-aware string.\n\n<CombinedTable client:load />\n\n## Formatting date ranges\n\n`Intl.DateTimeFormat` exposes a `formatRange(startDate, endDate)` method that intelligently collapses the shared parts of two dates β€” so instead of \"December 25, 2025 – January 3, 2026\" you only see the parts that actually differ. The hook returns the formatter instance directly, so `formatRange` is available without any extra work.\n\nReference range: **December 25, 2025 14:30 β†’ January 3, 2026 09:15**.\n\n<DateRangeDemo client:load />\n\n<Snippet>\n```tsx\nimport {useFormatDateTime} from '@staffbase/design/hooks';\n\nconst EventDuration = ({start, end}: {start: Date; end: Date}) => {\n const locale = navigator.language;\n const formatter = useFormatDateTime(locale, 'long', 'short');\n\n // \"December 25, 2025, 2:30 PM – January 3, 2026, 9:15 AM\"\n return <span>{formatter.formatRange(start, end)}</span>;\n};\n````\n\n</Snippet>\n\nUse `formatRangeToParts` for custom rendering β€” for example to bold the parts that differ between the two dates:\n\n<Snippet>\n```tsx\nconst formatter = useFormatDateTime('en-US', 'long', undefined);\nconst parts = formatter.formatRangeToParts(start, end);\n\nreturn (\n\n <span>\n {parts.map((part, i) =>\n part.source === 'shared' ? <span key={i}>{part.value}</span> : <strong key={i}>{part.value}</strong>,\n )}\n </span>\n);\n```\n</Snippet>\n\n## API reference\n\n### useFormatDateTime(locale, dateStyle, timeStyle)\n\n<PropTable\n rows={[\n {\n name: 'locale',\n type: ['string', 'string[]', 'Intl.Locale', 'Intl.Locale[]'],\n description: 'A BCP 47 language tag (or list of tags) passed to Intl.DateTimeFormat.',\n required: true,\n },\n {\n name: 'dateStyle',\n type: ['short', 'medium', 'long', 'undefined'],\n description: 'How the date portion is rendered. Pass undefined to omit the date.',\n required: true,\n },\n {\n name: 'timeStyle',\n type: ['short', 'medium', 'undefined'],\n description: 'How the time portion is rendered. Pass undefined to omit the time.',\n required: true,\n },\n ]}\n/>\n\nReturns an `Intl.DateTimeFormat` instance. Use `.format(date)`, `.formatRange(start, end)`, or `.formatRangeToParts(start, end)`."
218
+ },
219
+ {
220
+ "slug": "hooks/use-format-relative-time",
221
+ "title": "useFormatRelativeTime",
222
+ "description": "A React hook that returns a memoised formatting function for locale-aware relative time, like \"3 days ago\", \"yesterday\", or \"in 2 weeks\".",
223
+ "group": "Other",
224
+ "body": "<ComponentPreview>\n <RelativeTimeBasic client:load />\n <Fragment slot=\"code\">\n```tsx\nimport {useFormatRelativeTime} from '@staffbase/design/hooks';\n\nconst format = useFormatRelativeTime('en-US');\n\nformat({days: -3}); // \"3 days ago\"\nformat({days: -1}); // \"yesterday\"\nformat({weeks: 2}); // \"in 2 weeks\"\n\n````\n </Fragment>\n</ComponentPreview>\n\nPass a `Duration` object and get back a human-readable string. Backed entirely by the browser's [`Intl.RelativeTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat) API β€” no third-party dependency required.\n\nThe hook automatically picks the most significant unit from the `Duration` object: `years` first, then `months`, then `weeks`, then `days`. It uses `numeric: 'auto'`, which emits natural-language expressions wherever a locale has them β€” words like _\"yesterday\"_, _\"today\"_, _\"tomorrow\"_, _\"last week\"_ β€” instead of always emitting a numeric value.\n\n## Durations at a glance\n\nEvery supported duration value rendered for the selected locale.\n\n<DurationsTable client:load />\n\n## Usage\n\nThe hook returns a stable `format` function (memoised with `useCallback`). Call it anywhere in your render with a `Duration` object.\n\n<Snippet>\n```tsx\nimport {useFormatRelativeTime} from '@staffbase/design/hooks';\n\nconst TimeAgo = ({date}: {date: Date}) => {\n const locale = navigator.language; // or from your i18n context\n const format = useFormatRelativeTime(locale);\n\n const diffMs = date.getTime() - Date.now();\n const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24));\n\n return <time dateTime={date.toISOString()}>{format({days: diffDays})}</time>;\n};\n````\n\n</Snippet>\n\n### Unit priority\n\nThe hook resolves the most significant non-zero unit top-down. Provide only the units you want to consider:\n\n<Snippet>\n```tsx\nconst format = useFormatRelativeTime('en-US');\n\nformat({years: -1, months: -3, days: -10}); // \"last year\" β€” years wins\nformat({months: -3, days: -10}); // \"3 months ago\" β€” months wins\nformat({weeks: -1, days: -10}); // \"last week\" β€” weeks wins\nformat({days: -3}); // \"3 days ago\"\nformat({days: 0}); // \"today\"\n\n```\n</Snippet>\n\n## API reference\n\n### useFormatRelativeTime(locale)\n\n<PropTable\n rows={[\n {\n name: 'locale',\n type: ['string', 'string[]', 'Intl.Locale', 'Intl.Locale[]'],\n description: 'A BCP 47 language tag (or list of tags) passed to Intl.RelativeTimeFormat.',\n required: true,\n },\n ]}\n/>\n\nReturns a `format(duration)` function. The `Duration` object accepts optional `years`, `months`, `weeks`, and `days` numbers; positive values are in the future, negative in the past.\n```"
225
+ }
226
+ ]