From 0a5567fccc7c8632fcc4ca019d49ff8e8c54ff92 Mon Sep 17 00:00:00 2001
From: damann <67584870+damann@users.noreply.github.com>
Date: Mon, 13 Apr 2026 15:58:57 +0200
Subject: [PATCH 01/13] Create Forma 36 skill documentation
Add Forma 36 skill documentation for UI development using Contentful's design system, including rules, guidelines, and component usage.
---
skill/forma-36-skill | 874 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 874 insertions(+)
create mode 100644 skill/forma-36-skill
diff --git a/skill/forma-36-skill b/skill/forma-36-skill
new file mode 100644
index 0000000000..9b4628a783
--- /dev/null
+++ b/skill/forma-36-skill
@@ -0,0 +1,874 @@
+name: forma36
+description: Build UI using Forma 36, Contentful's design system. Use when the user references Forma 36, f36, Contentful components, or wants to build a Contentful app UI — including prototypes, screens, and interface explorations.
+---
+
+# Forma 36 — Contentful Design System
+
+Forma 36 is the design language of Contentful. Every prototype, screen, and component built for Contentful must look and feel like it belongs in the Contentful product. This skill ensures that.
+
+The goal is not to use the right component name — it's to produce UI that is indistinguishable from the real Contentful interface.
+
+## Scope, Authority, and Rule Interpretation
+
+This document is the primary operational specification for generating UI with Forma 36.
+
+Use it to determine:
+- which UI structures are allowed
+- which components and patterns to use
+- which visual and writing rules are mandatory
+- which implementations are forbidden
+
+This is a normative specification. Outputs must follow explicit rules. Philosophy sections explain intent but do not override rules or permit invention.
+
+---
+
+## Rule Types
+
+All guidance is expressed using the following rule types:
+
+- **Required** — mandatory in every applicable case. Treat as pass/fail.
+- **Forbidden** — never allowed. If present, the UI is incorrect.
+- **Preferred** — default choice unless a more specific rule overrides it.
+- **Context-dependent** — mandatory only when the stated condition is true.
+
+### Rule precedence
+
+Apply rules in this order:
+1. Forbidden
+2. Required
+3. Context-dependent (when condition is true)
+4. Preferred
+5. Philosophy and explanatory text
+
+When applying rules:
+- apply conditional rules only when their condition is true
+- when rules conflict, the more specific rule wins
+- Forbidden rules always take precedence
+
+Only explicit rules, named components, defined patterns, and tokenized values are normative.
+
+---
+
+## Supporting Files
+
+Supporting files extend this specification with domain-specific detail.
+
+Apply them as follows:
+- use this document first to determine structure and rule intent
+- use the relevant supporting file for detailed decisions
+- when rules do not conflict, apply both
+- when rules conflict, the more specific rule wins
+
+This skill is split across four reference files. Read the relevant one for the task at hand:
+
+| File | What's in it |
+|---|---|
+| `tokens.md` | Complete token reference — all colors, spacing, typography, shadows, border radius, z-index |
+| `components.md` | Every component: usage intent, key props, code examples, rules |
+| `screen-patterns.md` | Named layout patterns — list page, detail page, empty state, error state, confirmation dialog, wizard, dashboard |
+| `brand-guidelines.md` | Voice, tone, writing rules, icon style, photography guidance |
+
+---
+
+## Generation Constraints
+
+**Forbidden**
+- Do not invent new components.
+- Do not invent new layout patterns.
+- Do not invent new visual styles.
+- Do not invent new interaction models.
+- Do not invent new token values.
+- Do not substitute a custom solution when a defined component, primitive, or pattern exists.
+
+**Preferred**
+- Use defined Forma 36 components.
+- Use layout primitives: Box, Flex, Stack, Grid.
+- Use tokenized spacing, radius, color, and typography.
+- Use named patterns from screen-patterns.md when applicable.
+
+**Context-dependent**
+- If required information is missing, incomplete, or unspecified:
+ - do not guess beyond the defined system
+ - compose solutions using available primitives and defined components
+ - apply existing rule types and precedence
+ - prefer standard patterns over custom implementations
+
+## Fallback behavior
+
+These rules define how to proceed when required information is missing, incomplete, ambiguous, or in conflict. Apply them with the rule precedence defined in this document.
+
+### Missing component definition
+
+**Required**
+- If a needed component is not defined in this document or the supporting files, do not invent a new component.
+- Compose the UI using defined components and layout primitives only.
+- Prefer the simplest composition that satisfies the requirement using Box, Flex, Stack, Grid, and defined Forma 36 components.
+
+**Forbidden**
+- Do not create a custom component, custom variant, or custom API to fill the gap.
+
+### Missing layout or pattern
+
+**Required**
+- If a needed layout or named pattern is not defined, use the standard application shell defined in this document.
+- Compose the screen from the existing header, content area, sidebar, and layout primitives only.
+- Prefer a standard composition over a custom layout.
+
+**Forbidden**
+- Do not invent a new page pattern, navigation pattern, or layout structure.
+
+### Missing interaction or state behavior
+
+**Required**
+- If a component-specific interaction rule is missing, use the global Interaction states section as the source of truth.
+- Keep behavior minimal and within the defined component model.
+
+**Context-dependent**
+- If a state is not applicable to a component, omit it.
+
+**Forbidden**
+- Do not invent a new interaction model, custom animation, or undefined state behavior.
+
+### Missing accessibility rule
+
+**Required**
+- If a component-specific accessibility rule is missing, apply the explicit accessibility rules already defined in this document for labels, keyboard operation, focus visibility, heading structure, state communication, and text alternatives.
+- Prefer native semantics and documented component APIs over custom accessibility implementations.
+
+**Forbidden**
+- Do not assume undocumented accessibility behavior for a component.
+- Do not remove an accessibility requirement because a component-specific rule is missing.
+
+### Conflicting rules
+
+**Required**
+- Apply rule precedence defined in this document.
+- When rules conflict, the more specific rule wins.
+- When a global rule and a component-specific rule conflict, use the component-specific rule if it does not violate a Forbidden rule.
+
+**Forbidden**
+- Do not merge conflicting rules into a compromise implementation.
+- Do not ignore a Forbidden rule because another rule is less strict.
+
+### Ambiguous or underspecified requirements
+
+**Required**
+- If the requirement is underspecified, select the simplest valid option that is explicitly supported by this document.
+- Prefer defined components over composition, and composition over omission, when both remain within documented rules.
+- If a requirement cannot be satisfied without inventing a component, pattern, behavior, or rule, omit the unsupported part.
+
+**Forbidden**
+- Do not approximate with a “close enough” custom solution.
+- Do not infer undocumented product behavior from philosophy text, examples, or visual style alone.
+
+### Safe default rules
+
+**Required**
+- Stay within defined tokens, defined components, defined patterns, and defined state rules.
+- Prefer standard, lower-complexity compositions over dense or highly customized layouts.
+- Preserve existing explicit constraints even when information is missing elsewhere.
+
+**Forbidden**
+- Do not add decorative UI, auxiliary controls, or optional interactions that are not required by the specification.
+
+## Design Philosophy
+
+This section is non-normative. It explains intent. It does not create requirements, override explicit rules, or permit invention. Use it only to interpret explicit rules elsewhere in this document.
+
+Forma 36 is built on one idea: software should feel calm, trustworthy, and focused.
+
+Supporting principles:
+
+1. **Clarity over cleverness**
+Prioritize task clarity over decorative or novel presentation.
+
+2. **Restraint as a design decision**
+Prefer lower visual intensity, clear hierarchy, and semantic use of color.
+
+3. **Consistency builds trust**
+Prefer existing patterns, consistent spacing, consistent component behavior, and consistent language.
+
+4. **Accessibility is non-negotiable**
+Apply the explicit accessibility, keyboard, focus, labeling, and state communication rules defined elsewhere in this document.
+
+---
+
+## Visual Language
+
+### Color
+
+| Role | Color | Hex | Use |
+|---|---|---|---|
+| Primary action | Blue 600 | #0059C8 | Primary interactive action |
+| Success / published | Green 600 | #006D23 | Positive outcomes, published state |
+| Danger / error | Red 600 | #BD002A | Destructive actions, errors |
+| Warning / draft | Orange 500 | #CC4500 | Caution, unpublished or draft state |
+| Premium / new | Purple 600 | #6B3DB5 | Paid features |
+| UI surface | Gray 100 | #F7F9FA | Page backgrounds |
+| Panel surface | White | #FFFFFF | Cards, sidebars, modals |
+| Default text | Gray 900 | #111B2B | Body copy, labels |
+| Secondary text | Gray 700 | #414D63 | Section headings, metadata |
+| Muted text | Gray 600 | #536171 | Hints, inactive labels |
+| Borders / dividers | Gray 200 | #E7EBEE | Dividers between sections |
+
+**Supported shade sets**
+- blue100 to blue900
+- gray100 to gray900
+- green100 to green900
+- orange100 to orange900
+- purple100 to purple900
+- red100 to red900
+- yellow100 to yellow900
+
+**Required**
+- Use only supported shade sets and defined color roles.
+- Pair semantic color with text or icon when the color communicates meaning.
+- Use Blue 600 only for primary interactive actions.
+- Use Purple 600 only for paid features.
+- Use blue/100 (#E8F5FF) as the active navigation background.
+- Use black text darker than gray-800 only on shades 100 to 400.
+- Use white text only on shades 500 to 900.
+- For non-yellow same-color foreground/background combinations, use only:
+ - 600 on 100
+ - 600 on 200
+ - 700 on 300
+ - 300 on 900
+ - 500 on white
+ - 500 on gray100
+- For yellow same-color foreground/background combinations, use only:
+ - 800 on 100
+ - 800 on 200
+ - 900 on 300
+ - 300 on 900
+ - 800 on white
+ - 800 on gray100
+
+**Forbidden**
+- Do not use color as decoration.
+- Do not rely on color alone to communicate meaning.
+- Do not use blue for non-interactive decoration.
+- Do not use a blue text color for active navigation items.
+- Do not use unsupported foreground/background shade combinations.
+- Do not use non-yellow same-color combinations outside the allowed set.
+- Do not use yellow same-color combinations outside the allowed yellow set.
+- Do not use yellow when another defined semantic role matches the meaning.
+
+### Typography
+
+| Role | Size | Weight | Use |
+|---|---|---|---|
+| Display | 1.5rem+ | 600 | Page titles, hero text |
+| Section heading | 0.75rem | 600 | Nav group labels, table headers |
+| Body (default) | 0.875rem | 400 | Interface copy |
+| Body medium | 0.875rem | 500 | Emphasized labels |
+| Small / caption | 0.75rem | 400 | Metadata, hints |
+
+**Required**
+- Use Geist as the typeface.
+- Import from https://cdn.f36.contentful.com.
+- Use fallback fonts -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif.
+- Use font-feature-settings: 'ss05' 1 on all Geist text.
+- Use line height 1.5 by default.
+- Use line height 1.25 only for nav items and badges.
+- Use letter spacing 0 for all sizes.
+- Use sentence case for labels.
+
+**Forbidden**
+- Do not mix more than two font weights on a single screen.
+- Do not use Title Case for labels.
+- Do not use ALL CAPS for labels.
+
+### Spacing
+
+| Token | Value | Common use |
+|---|---|---|
+| 2xs | 4px | Icon gaps, tight groupings |
+| xs | 8px | Between related items |
+| s | 12px | Component internal padding |
+| m | 16px | Standard padding |
+| l | 24px | Section padding |
+| xl | 32px | Large section gaps |
+| 2xl | 48px | Page-level separation |
+
+**Required**
+- Use only the defined spacing tokens.
+- Manage external spacing with layout wrappers: Flex, Stack, Box.
+- Use token names on layout components: spacingXs, spacingS, spacingM, etc.
+
+**Forbidden**
+- Do not use arbitrary pixel values for spacing.
+- Do not assign external spacing directly to components.
+
+### Shadows
+
+| Name | Value | Use |
+|---|---|---|
+| Default | 0 0 0 1px rgba(25,37,50,.10), 0 3px 7px -3px rgba(25,37,50,.10), 0 6px 12px -2px rgba(25,37,50,.10) | Panels, sidebar, content area |
+| Button | 0 1px 0 rgba(17,27,43,.05) | Secondary buttons |
+
+**Required**
+- Use Shadow/Default for panels, sidebar, and content area.
+- Use Shadow/Button for secondary buttons.
+
+**Forbidden**
+- Do not create custom shadows.
+- Do not create glows.
+
+### Borders & Radius
+
+| Element | Radius |
+|---|---|
+| Panels, cards | 12px (top corners only for flush-to-edge panels) |
+| Buttons, inputs, nav items | 6px |
+| Small chips, badges | 4px |
+| Avatars | 100px (pill) |
+
+**Required**
+- Use only the defined radius values.
+- Use 12px for panels and cards.
+- Use top corners only for flush-to-edge panels.
+- Use 6px for buttons, inputs, and nav items.
+- Use 4px for small chips and badges.
+- Use 100px for avatars.
+
+**Forbidden**
+- Do not use border radius values outside 4px, 6px, 12px, and 100px.
+
+---
+
+## Base Layout
+
+**Required**
+- Every screen uses this exact shell.
+- Do not invent alternative chrome.
+
+Figma reference: `https://www.figma.com/design/CHqkCTVTpvkqDNXtoAR5cE/Experiences-Library?node-id=6764-6490`
+
+┌──────────────────────────────────────────────────────┐
+│ Top bar (52px, gray-100 background) │
+├───────────────┬──────────────────────────────────────┤
+│ │ Content area │
+│ Sidebar │ ┌────────────────────────────────┐ │
+│ (240px) │ │ Header (title + actions) │ │
+│ │ ├────────────────────────────────┤ │
+│ │ │ Page content │ │
+│ │ └────────────────────────────────┘ │
+└───────────────┴──────────────────────────────────────┘
+[8px agent strip]
+
+### Top bar
+
+**Required**
+- Height: `52px`.
+- Background: `gray/100`.
+- Horizontal padding: `24px` left, `64px` right.
+- The `64px` right gap is reserved for the agent strip.
+- Left content order: Contentful `"C"` logo → Space/Environment switcher pill.
+- Right content order: Search icon → Help icon → User avatar (`24px`).
+- Environment switcher background: `green/100`.
+- Environment switcher border: `green/400`.
+- Environment switcher text: `"Space > Production"` in `green/700`.
+- Environment switcher includes a `green-300` right-edge indicator strip.
+- Environment switcher includes a rocket icon.
+
+**Forbidden**
+- Do not change the top bar height.
+- Keep the top bar content order consistent with the defined structure.
+- Do not use an alternative top bar structure.
+
+### Sidebar
+
+#### Structure
+
+**Required**
+- Sidebar is present in the application shell.
+- Sidebar includes a collapse button.
+- Collapse button is a transparent `IconButton` with `CaretLeft` icon.
+- Collapse button position: bottom-right of sidebar.
+- Sidebar includes all defined sections, items, icons, and states.
+
+**Forbidden**
+- Do not stub the sidebar.
+- Do not simplify the sidebar.
+- Do not remove defined sections, items, icons, or states.
+
+#### Dimensions
+
+**Required**
+- Width: `240px`.
+- Background: `white`.
+- Shadow: `Shadow/Default`.
+- Top corners radius: `12px`.
+- Padding: `pt-12px pb-8px px-4px`.
+- Inner menu padding: `px-12px`.
+- Gap between sections: `12px`.
+- Nav item height: `32px`.
+- Nav item padding: `px-12px py-6px`.
+- Nav item gap: `8px`.
+- Nav item border radius: `4px`.
+- Section title size: `12px`.
+- Section title weight: `600`.
+- Section title color: `gray/700`.
+- Section title height: `28px`.
+- Section title left padding: `pl-12px`.
+
+#### Content
+
+**Required**
+- Sections are separated by `gray/200` bottom border.
+- Sidebar sections and items:
+ 1. **Setup** — Design System *(active by default)*, Content Model
+ 2. **Creation** — Experiences, Content
+ 3. **Optimization** — Optimizations, Analytics
+ 4. **Platform Configuration** — Collections, AI & Automations, Taxonomy, Apps
+ 5. *(no title)* — Settings
+
+#### States
+
+**Required**
+- See Interaction states.
+- Nav item content: Icon (`16px`, duotone variant for active) + label (body regular, `gray/900`) + optional caret right.
+- Active state background: `blue/100` (`#E8F5FF`).
+- Default state background: transparent.
+- Hover state background: `gray/100`.
+
+#### Constraints
+
+**Required**
+- Implement the sidebar faithfully.
+- Replicate all sections, items, icons, and states exactly.
+
+**Forbidden**
+- Do not modify sidebar structure outside the defined shell.
+- Do not invent additional sidebar patterns.
+
+### Content area
+
+**Required**
+- Content area fills the remaining width.
+- Background: `white`.
+- Shadow: `Shadow/Default`.
+- Top corners radius: `12px`.
+- Padding: `pt-12px px-24px`.
+- `Header` is always present at the top of the content area.
+- Use `Title` variant for top-level pages.
+- Use `Breadcrumb` variant for subpages.
+- Page content is placed below the `Header`.
+
+**Context-dependent**
+- Set `dividerLine={true}` when the content area has a right sidebar.
+
+**Forbidden**
+- Do not omit the `Header`.
+- Do not place page content above the `Header`.
+
+---
+
+## Layout rules
+
+### Layout Primitives
+
+**Required**
+- Use layout primitives to compose all layout structure outside the application shell.
+- Use only Flex, Stack, Box, and Grid for layout composition.
+- Use tokenized spacing values on layout primitives: spacingXs, spacingS, spacingM, spacingL, spacingXl, spacing2Xl.
+- Use layout primitives to control external spacing between components.
+- Keep component external spacing outside the component itself.
+- Keep layout composition inside the existing application shell defined in this document.
+
+**Preferred**
+- Use Stack for one-directional vertical grouping.
+- Use Flex for one-directional layouts that require explicit alignment, justification, or wrapping behavior.
+- Use Grid for two-dimensional layouts with rows and columns.
+- Use Box for single-region containment, padding, margin, width, or other block-level layout control.
+- Use the simplest layout primitive that satisfies the requirement.
+- Prefer Stack over Flex when only directional spacing is required.
+- Prefer Flex over Grid when the layout is one-dimensional.
+
+**Required**
+- Compose layouts from primitives first, then place defined components inside them.
+- Use nested primitives when one primitive cannot express the required structure.
+- Keep DOM order aligned with visual order.
+- Keep page-level spacing, section spacing, and inter-component spacing on layout primitives, not on child components.
+- Use existing shell regions as fixed boundaries: top bar, sidebar, content area, header, page content.
+- Keep the Header at the top of the content area.
+- Place unrestricted page content below the Header.
+
+**Context-dependent**
+- When content requires explicit columns or row/column placement, use Grid.
+- When content is arranged in a single vertical flow, use Stack.
+- When items must be aligned horizontally or distributed along one axis, use Flex.
+- When only padding, margin, width, or a single container wrapper is needed, use Box.
+- When spacing between elements is required, use spacing tokens only.
+- When a right sidebar is present in the content area, follow the existing Header rule for dividerLine={true}.
+- If a layout requirement is underspecified, use the simplest valid composition built from Box, Flex, Stack, and Grid.
+- If a named pattern is missing, compose the layout from the standard application shell and layout primitives only.
+
+**Forbidden**
+- Do not invent new layout primitives.
+- Do not invent new layout patterns outside the application shell and named patterns defined elsewhere in this document.
+- Do not use arbitrary pixel values for layout spacing.
+- Do not use components as layout substitutes when a layout primitive is sufficient.
+- Do not assign external spacing directly to components.
+- Do not use Grid for one-dimensional layouts.
+- Do not use Flex or Stack to approximate a two-dimensional grid layout.
+- Do not break the shell structure defined in Base Layout.
+- Do not reorder DOM structure to create a visual layout that changes reading or interaction order.
+
+---
+
+## Component rules
+
+### Buttons
+
+**Required**
+- Use `Button` for button actions.
+- Use `Primary` for the single most important action in a context. Maximum: one per context.
+- Use `Secondary` for supporting actions.
+- Use `Positive` for constructive actions: create, publish, add.
+- Use `Negative` for destructive or irreversible actions: delete, remove, archive.
+- Use `Transparent` only on light backgrounds.
+- Use native button semantics for button actions.
+- Icon-only actions must use `IconButton` or `ToggleButton` with a descriptive `aria-label`.
+- Button groups must expose an `aria-label`.
+
+**Context-dependent**
+- Use `small` for toolbars.
+- Use `medium` as the default size.
+- Use `large` only for hero CTAs.
+- If a button is marked disabled through a custom button element or `role=button`, click and keydown handlers must not fire.
+- For state behavior, see **Interaction states**.
+- For loading behavior, see **Interaction & State rules**.
+
+**Forbidden**
+- Do not use more than one `Primary` button in the same context.
+
+### Forms
+
+**Required**
+- Every input must live inside a `FormControl`.
+- Structure is always: `Label → Input → Help text → Validation message`.
+- `FormControl.Label` is required.
+- `FormControl.HelpText` is for guidance that always shows.
+- Pass `isRequired`, `isInvalid`, and `isDisabled` to `FormControl`, not to the input directly.
+- Required form elements must be marked programmatically with `isRequired`, not only visually.
+- Help text and validation messages must stay inside the same `FormControl`.
+- `TextInput` and `Textarea` with separate labels must expose `id` for `htmlFor`.
+- Controls used in form submission must expose `name` when submission depends on it.
+- Choose the input type as follows:
+ - Short free text → `TextInput`
+ - Long free text → `Textarea`
+ - Pick one from a short list (`≤8` options) → `Select`
+ - Pick one from a long list → `Autocomplete`
+ - Toggle one option → `Checkbox`
+ - Pick one from a mutually exclusive group → `Radio.Group`
+
+**Context-dependent**
+- `FormControl.ValidationMessage` is for errors and appears only when invalid. See **Feedback selection rules**.
+- If there is no visible label, provide `aria-label` or `aria-labelledby` as supported by the control.
+- Use `Checkbox.Group` or an equivalent fieldset/legend structure for related checkbox options.
+- Use `Radio.Group` to group related radio buttons and provide a group label.
+- If a visible legend is not possible for checkbox or radio groups, use `aria-labelledby`.
+- `Switch` labels must describe the action being controlled.
+- If a visible label is not present for `Select`, provide `aria-label`.
+- Use `Datepicker` inside `FormControl` when label, help text, or validation is needed.
+- Icon-only `Datepicker` trigger buttons must provide a screen-reader label.
+- Disabled or invalid date states must be communicated in text, not only visually.
+- In `Multiselect`, if a visible label is not present, provide an `aria-label`.
+- For state behavior, see **Interaction states**.
+
+**Forbidden**
+- Do not use placeholder text as a replacement for a label.
+
+### Feedback & Status
+
+**Required**
+- Use `Notification` for immediate feedback after a user action.
+- `Notification` dismisses after 6 seconds.
+- `Notification.success('Entry published')` uses active voice and present tense.
+- `Notification.error(...)` must be specific and actionable.
+- Use `Note` for proactive context the user needs before acting.
+- Place `Note` near the relevant content, not at the top of the page generically.
+- Badge labels and status indicators stay to one word.
+- Use `EntityStatusBadge` for Contentful entity status.
+
+**Context-dependent**
+- `Note` variants: neutral, primary, positive, warning, negative, premium.
+- Badge status mapping:
+ - positive = Published
+ - warning = Draft
+ - negative = Deleted
+- For selection logic, see **Feedback selection rules**.
+
+**Forbidden**
+- Do not use `Notification` for persistent context.
+- Do not construct a raw `Badge` for Contentful entity status.
+
+### Navigation & Overlays
+
+**Required**
+- For `Modal` usage, see **Feedback selection rules**.
+- `Modal` content must include a clear title and description.
+- Use `Menu` for actions and navigation choices triggered by a button.
+- `Tabs` navigate between sections of related content.
+- Maximum tabs: `6`.
+- Tab labels must accurately describe the panel content.
+- Provide a unique `panelId` for each tab-panel relationship.
+- `Tooltip` is supplementary, non-critical context.
+- Tooltip content must be short.
+- Always include a unique `id` prop on `Tooltip`.
+- If the trigger is icon-only, the trigger must have its own accessible label independent of the tooltip text.
+
+**Context-dependent**
+- Focus moves into the modal when it opens.
+- Focus remains trapped inside the modal while it is open.
+- Focus returns to the invoking control when the modal closes.
+- Modal dialogs close with `Escape` and outside click unless disabled.
+- Menus move focus into the menu when they open and support keyboard navigation among items.
+- For state behavior, see **Interaction states**.
+
+**Forbidden**
+- Do not use `Select` for actions.
+- Do not put links, buttons, or forms inside a `Tooltip`.
+
+---
+
+## Interaction & State rules
+
+This section defines behavior and interaction rules. Use it with **Component rules**, **Interaction states**, and **Feedback selection rules**. Do not restate component selection or structural rules here.
+
+### Sidebar
+
+**Required**
+- Sidebar navigation uses the global **Interaction states** model.
+- The active item is `Design System` by default.
+- The collapse control is an `IconButton` with `CaretLeft`.
+
+**Context-dependent**
+- Active nav items use `blue/100` (`#E8F5FF`) background fill.
+- Default nav items use transparent background.
+- Hovered nav items use `gray/100` background fill.
+- Active nav items use the duotone icon variant.
+
+### Content area
+
+**Required**
+- See **Base Layout**.
+
+### Buttons
+
+**Required**
+- Buttons use the global **Interaction states** model.
+- Loading uses `isLoading`.
+
+**Forbidden**
+- Do not disable a button and add a spinner manually.
+
+### Forms
+
+**Required**
+- Forms use the global **Interaction states** model.
+
+### Feedback & Status
+
+**Required**
+- See **Feedback selection rules**.
+
+### Navigation & Overlays
+
+**Required**
+- `Modal` behavior follows the global **Interaction states** model.
+- Menus support keyboard navigation among items.
+- Tooltips are non-critical and supplementary.
+
+**Context-dependent**
+- Focus moves into the menu when it opens.
+- Focus moves into the modal when it opens.
+- Focus remains trapped inside the modal while it is open.
+- Focus returns to the invoking control when the modal closes.
+- Modal closes with `Escape` and outside click unless disabled.
+
+### Keyboard and focus
+
+**Required**
+- All interactive elements are keyboard operable.
+- Tab order follows the rendered interaction order.
+
+### Semantic structure and state communication
+
+**Required**
+- Semantic DOM order matches visual order.
+
+### Header and layout accessibility
+
+**Required**
+- Breadcrumbs and back buttons have descriptive accessible labels.
+- Interactive elements placed in header actions or filters are keyboard accessible and clearly labeled.
+- Interactive content placed inside layout containers preserves keyboard navigation and focus management.
+
+**Context-dependent**
+- If multiple headers are present, use the `as` prop to avoid duplicate landmarks and preserve heading hierarchy.
+
+---
+
+## Feedback selection rules
+
+### Selection logic
+
+**Required**
+- Use FormControl.ValidationMessage when the message describes an error on a specific input.
+- Use Note when the message must remain visible while the user reviews or acts on related content.
+- Use Notification when the message confirms the result of a user action and can dismiss after 6 seconds.
+- Use Modal when the user must confirm before proceeding or when the information is essential to the current workflow.
+
+### FormControl.ValidationMessage
+
+**Use when**
+
+**Context-dependent**
+- the message is tied to a specific field
+- the field is invalid
+
+**Do not use when**
+
+**Forbidden**
+- the message applies to the page, section, or workflow rather than a specific input
+- the message is only informational guidance
+
+### Note
+
+**Use when**
+
+**Context-dependent**
+- the message is proactive context the user needs before acting
+- the message must remain visible
+- the message belongs near the relevant content
+
+**Do not use when**
+
+**Forbidden**
+- the message is a field-specific validation error
+- the message is only a temporary action result
+- the message is placed at the top of the page generically
+
+### Notification
+
+**Use when**
+
+**Context-dependent**
+- the message is immediate feedback after a user action
+- the message can dismiss after 6 seconds
+- the message is an action result such as success or error
+
+**Do not use when**
+
+**Forbidden**
+- the message must remain visible while the user works
+- the message is persistent context
+- the message replaces inline validation for a specific field
+
+### Modal
+
+**Use when**
+
+**Context-dependent**
+- the user must confirm before proceeding
+- the information is essential to the current workflow
+- the interaction blocks the interface
+
+**Do not use when**
+
+**Forbidden**
+- the information is non-essential
+- the interaction interrupts high-stakes flows without confirmation need
+- the message is only a transient action result
+- the message is only persistent inline context
+
+### Forbidden patterns
+
+**Forbidden**
+- Do not use Notification for persistent context
+- Do not use Note for field validation
+- Do not use Modal for non-essential information
+- Do not use Modal for a transient success or error message
+- Do not use FormControl.ValidationMessage for page-level or section-level messaging
+
+### Acceptance criteria
+
+**Required**
+- A field-specific error uses FormControl.ValidationMessage
+- A message that must remain visible uses Note
+- A temporary action result uses Notification
+- A blocking confirmation uses Modal
+- Notification messages dismiss after 6 seconds
+- Note is placed near the relevant content
+- FormControl.ValidationMessage appears only when invalid
+- Modal is used only for confirmation before proceeding or information essential to the current workflow
+
+---
+
+## Content & writing
+
+### Writing rules
+
+**Required**
+- Use active voice.
+- Use present tense.
+- Use sentence case everywhere.
+- Start button labels with a verb.
+- Start link labels with a verb when the link is an action.
+- Error messages must state what went wrong and what to do next.
+- Confirmation dialogs for destructive actions must state exactly what will be destroyed.
+
+**Forbidden**
+- Do not use "please".
+- Do not use "click here".
+- Do not use "learn more" without context.
+- Do not use vague single-word labels such as "Go" or "Submit".
+- Do not use Title Case.
+- Do not use ALL CAPS.
+
+**Context-dependent**
+- If a link is navigation rather than an action, the label must describe the destination clearly.
+
+### Button writing rules
+
+**Required**
+- Button labels start with a verb.
+- Maximum button label length: 3 words.
+- The dismiss action for a destructive flow uses Never mind.
+
+**Forbidden**
+- Do not use Cancel as the dismiss action for a destructive flow.
+
+### Modal writing rules
+
+**Required**
+- Modal title format: verb + noun.
+- Confirm button format: verb + noun.
+- Destructive confirmation copy states exactly what will be destroyed.
+- The dismiss action for a destructive flow uses Never mind.
+
+**Forbidden**
+- Do not use a vague modal title.
+- Do not use a vague confirm label.
+- Do not use Cancel as the dismiss action for a destructive flow.
+
+---
+
+## What Forma 36 Never Does
+
+**Forbidden**
+- Do not use decorative color.
+- Do not create custom shadows or glows.
+- Do not use rounded corners outside 4px, 6px, 12px, and 100px.
+- Do not use arbitrary spacing.
+- Do not use Title Case labels.
+- Do not use more than one primary button per screen section.
+- Do not use Button as a link. Use TextLink for navigation.
+- Do not use a bare Badge for entity status. Use EntityStatusBadge.
+- Do not use Notification for persistent context. Use Note and follow **Feedback selection rules**.
+- Do not place interactive elements inside Tooltip content.
+- Do not omit the Header from a page.
+- Do not use Workbench. Use Layout.
+- Do not use raw hex colors in code. Use tokens from @contentful/f36-tokens.
From f0032bed983de76434fc53a241f5810e5b8c0198 Mon Sep 17 00:00:00 2001
From: damann <67584870+damann@users.noreply.github.com>
Date: Mon, 13 Apr 2026 16:24:17 +0200
Subject: [PATCH 02/13] Rename forma-36-skill to SKILL.md
---
skill/forma-36-skill => skills/SKILL.md | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename skill/forma-36-skill => skills/SKILL.md (100%)
diff --git a/skill/forma-36-skill b/skills/SKILL.md
similarity index 100%
rename from skill/forma-36-skill
rename to skills/SKILL.md
From e3e3ff330e1e5c8ffc54efbb2af9da7115670c77 Mon Sep 17 00:00:00 2001
From: damann <67584870+damann@users.noreply.github.com>
Date: Mon, 13 Apr 2026 16:25:51 +0200
Subject: [PATCH 03/13] Add tokens skill
This file contains design tokens for Forma 36, including color, spacing, typography, shadow, border radius, and z-index tokens. It emphasizes the use of token names instead of raw values to maintain consistency with the design system.
---
skills/tokens.md | 218 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 218 insertions(+)
create mode 100644 skills/tokens.md
diff --git a/skills/tokens.md b/skills/tokens.md
new file mode 100644
index 0000000000..3d856b03d9
--- /dev/null
+++ b/skills/tokens.md
@@ -0,0 +1,218 @@
+# Forma 36 — Design Tokens
+
+All values come from `@contentful/f36-tokens`. Never use raw hex values or arbitrary numbers in code — always use the token name. This ensures the UI stays in sync with the design system.
+
+```js
+import tokens from '@contentful/f36-tokens';
+// or individual tokens:
+import { colorPrimary, spacingM } from '@contentful/f36-tokens';
+```
+
+---
+
+## Color Tokens
+
+### Semantic (use these first)
+
+These are the tokens to reach for by default. They express intent, not shade.
+
+| Token | Value | Meaning |
+|---|---|---|
+| `colorPrimary` | `#0059C8` | Primary interactive actions, links |
+| `colorPositive` | `#006D23` | Success, published state, constructive |
+| `colorNegative` | `#BD002A` | Errors, destructive actions, deleted |
+| `colorWarning` | `#CC4500` | Warnings, draft/unpublished state |
+| `colorTextDark` | `#111B2B` | Primary text — highest contrast |
+| `colorTextBase` | `#1B273A` | Default body text |
+| `colorTextMid` | `#414D63` | Secondary labels, section titles |
+| `colorTextLight` | `#5A657C` | Tertiary text, captions |
+| `colorTextLightest` | `#67728A` | Placeholder text, inactive |
+
+### Gray Scale
+
+Used for surfaces, borders, and text.
+
+| Token | Hex | Common use |
+|---|---|---|
+| `gray100` | `#F7F9FA` | Page background |
+| `gray200` | `#E7EBEE` | Dividers, subtle borders |
+| `gray300` | `#CFD9E0` | Borders, input outlines |
+| `gray400` | `#B4C3CC` | Disabled borders |
+| `gray500` | `#8D9BA6` | Muted icons |
+| `gray600` | `#536171` | Muted/secondary icons |
+| `gray700` | `#414D63` | Section heading text |
+| `gray800` | `#2A3546` | Strong secondary text |
+| `gray900` | `#111B2B` | Primary text, nav labels |
+
+### Blue (Primary)
+
+| Token | Hex | Common use |
+|---|---|---|
+| `blue100` | `#E8F5FF` | Active nav item background |
+| `blue200` | `#CCE9FF` | Hover on primary elements |
+| `blue300` | `#A0D2FD` | — |
+| `blue400` | `#62B0F8` | — |
+| `blue500` | `#036FE3` | — |
+| `blue600` | `#0059C8` | **Primary action color** — buttons, links, active icons |
+| `blue700` | `#0044A8` | Pressed state |
+| `blue800` | `#003080` | — |
+| `blue900` | `#002A8E` | — |
+
+### Green (Positive)
+
+| Token | Hex | Common use |
+|---|---|---|
+| `green100` | `#EAF9E8` | Environment switcher background |
+| `green300` | `#9ED696` | Environment type indicator strip |
+| `green400` | `#5DB057` | Environment switcher border |
+| `green600` | `#006D23` | Published status, success text |
+| `green700` | `#00550C` | Environment switcher text |
+
+### Red (Negative)
+
+| Token | Hex | Common use |
+|---|---|---|
+| `red100` | `#FFF2F2` | Error field background |
+| `red600` | `#BD002A` | Error text, destructive button |
+| `red700` | `#9A0020` | Pressed destructive state |
+
+### Orange (Warning)
+
+| Token | Hex | Common use |
+|---|---|---|
+| `orange100` | `#FFF2E4` | Warning note background |
+| `orange400` | `#F07C00` | Warning icons |
+| `orange500` | `#CC4500` | Draft/unpublished state, warning text |
+
+### Purple (Premium — use sparingly)
+
+| Token | Hex | Common use |
+|---|---|---|
+| `purple100` | `#F7F2FF` | Premium feature background |
+| `purple600` | `#8553E7` | Premium label, "new feature" highlight |
+
+### Yellow (last resort only)
+
+| Token | Hex | Note |
+|---|---|---|
+| `yellow400` | `#FFC835` | Only when all other options fail |
+
+### Absolute
+
+| Token | Hex |
+|---|---|
+| `colorWhite` | `#FFFFFF` |
+| `colorBlack` | `#0C141C` |
+
+---
+
+## Spacing Tokens
+
+Components carry no external margin. Use these to create space between elements.
+
+| Token | Value | Rem |
+|---|---|---|
+| `spacing2Xs` | 4px | 0.25rem |
+| `spacingXs` | 8px | 0.5rem |
+| `spacingS` | 12px | 0.75rem |
+| `spacingM` | 16px | 1rem |
+| `spacingL` | 24px | 1.5rem |
+| `spacingXl` | 32px | 2rem |
+| `spacing2Xl` | 48px | 3rem |
+| `spacing3Xl` | 64px | 4rem |
+| `spacing4Xl` | 80px | 5rem |
+
+**Usage:**
+```js
+// Inline style
+
+
+// Layout component prop
+
+
+```
+
+---
+
+## Typography Tokens
+
+### Font Families
+
+| Token | Stack |
+|---|---|
+| `fontStackPrimary` | `Geist Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif` |
+| `fontStackMonospace` | `Geist Mono, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace` |
+
+Geist is loaded from `https://cdn.f36.contentful.com`. Always use `fontStackPrimary` — never hardcode `Geist` alone without the fallback stack.
+
+### Font Sizes
+
+| Token | Value | Use |
+|---|---|---|
+| `fontSize4Xl` | 3rem | Hero display text |
+| `fontSize3Xl` | 2.25rem | Large display |
+| `fontSize2Xl` | 1.5rem | Page-level headings |
+| `fontSizeXl` | 1.25rem | Section headings |
+| `fontSizeL` | 1rem | Large body |
+| `fontSizeM` | 0.875rem | **Default body** — most interface text |
+| `fontSizeS` | 0.75rem | Captions, metadata, section labels |
+
+### Font Weights
+
+| Token | Value | Use |
+|---|---|---|
+| `fontWeightNormal` | 400 | All body text |
+| `fontWeightMedium` | 500 | Emphasized labels, active states |
+| `fontWeightDemiBold` | 600 | Headings, section titles, nav group labels |
+
+### Line Heights
+
+| Token | Value | Use |
+|---|---|---|
+| `lineHeightDefault` | 1.5 | Body text |
+| `lineHeightCondensed` | 1.25 | Nav items, badges, compact UI |
+
+### Letter Spacing
+
+| Token | Value |
+|---|---|
+| `letterSpacingDefault` | 0rem |
+| `letterSpacingWide` | 0.1rem |
+
+Forma 36 uses `0` letter spacing by default. Do not add tracking to Geist.
+
+---
+
+## Shadow Tokens
+
+| Name | CSS Value | Use |
+|---|---|---|
+| `shadowDefault` | `0 0 0 1px rgba(25,37,50,.10), 0 3px 7px -3px rgba(25,37,50,.10), 0 6px 12px -2px rgba(25,37,50,.10)` | Panels, sidebar, content area, cards |
+| `shadowButton` | `0 1px 0 rgba(17,27,43,.05)` | Secondary and transparent buttons |
+
+Only these two shadows exist in Forma 36. Do not invent custom shadows.
+
+---
+
+## Border Radius Tokens
+
+| Token | Value | Use |
+|---|---|---|
+| `borderRadiusSmall` | 4px | Nav items, badges, small chips |
+| `borderRadiusMedium` | 6px | Buttons, inputs, menus |
+| `borderRadiusLarge` | 12px | Panels, cards, modals |
+| `borderRadiusPill` | 100px | Avatars, pill badges |
+
+Top-only radius (panels flush to bottom edge): apply `border-radius: 12px 12px 0 0`.
+
+---
+
+## Z-Index Tokens
+
+| Token | Value | Use |
+|---|---|---|
+| `zIndexDefault` | 0 | Normal flow |
+| `zIndexSticky` | 101 | Sticky headers |
+| `zIndexDropdown` | 200 | Menus, dropdowns |
+| `zIndexModal` | 1000 | Modals, dialogs |
+| `zIndexNotification` | 9000 | Toast notifications |
From 4c6d7c78369fdc82b5278c7317285d1aadfc8879 Mon Sep 17 00:00:00 2001
From: damann <67584870+damann@users.noreply.github.com>
Date: Mon, 13 Apr 2026 16:27:25 +0200
Subject: [PATCH 04/13] Add UI Patterns skill
---
skills/ui-patterns.md | 255 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 255 insertions(+)
create mode 100644 skills/ui-patterns.md
diff --git a/skills/ui-patterns.md b/skills/ui-patterns.md
new file mode 100644
index 0000000000..e2a89d6d71
--- /dev/null
+++ b/skills/ui-patterns.md
@@ -0,0 +1,255 @@
+# Forma 36 — UI Patterns
+
+Named, reusable patterns for the most common Contentful UI screens. When a design calls for one of these, use this as the structural template — not a blank canvas.
+
+---
+
+## 1. List / Index Page
+
+The most common pattern in Contentful. A collection of items (entries, content types, assets, apps) with filtering and primary action.
+
+```
+Header
+ title: "Experiences"
+ actions: [secondary button, primary button]
+ filters: [search input, optional filter selects]
+
+Content area
+ ├── Bulk action bar (appears when items are selected)
+ ├── Table or EntityList
+ │ rows: [thumbnail?, title, metadata columns, status badge, actions menu]
+ └── Pagination
+```
+
+**Rules:**
+- Primary action ("Create experience") is always top-right in the Header
+- Empty state shows when no items exist OR when search returns no results (different messages for each)
+- Row actions via `Menu` with `IconButton` trigger (MoreHorizontal icon) — not inline buttons
+- Status column uses `EntityStatusBadge`
+- Bulk selection: checkbox column left, bulk action bar appears above table
+- Loading state: show `Skeleton.Row` inside the table body
+
+**Empty state (no items):**
+- Heading: "No [items] yet"
+- Body: one sentence describing the purpose of this section
+- Action: primary button to create the first item
+
+**Empty state (filtered, no results):**
+- Heading: "No results for "[query]""
+- Body: "Try a different search term or clear filters."
+- Action: TextLink "Clear filters"
+
+---
+
+## 2. Detail / Edit Page
+
+A single entity being viewed or edited. Common for entries, content types, experience editors.
+
+```
+Header
+ title: entry name (or "Untitled" if new)
+ breadcrumbs: [parent section]
+ actions: [secondary actions menu, "Save" or "Publish" primary]
+
+Content area (two-column when sidebar present)
+ ├── Main content area (fields, editor, canvas)
+ └── Right sidebar: status, metadata, publish controls, linked entries
+```
+
+**Rules:**
+- `dividerLine={true}` on Header when right sidebar is present
+- Status in sidebar uses `EntityStatusBadge`
+- Publish button is `positive` variant — "Publish"
+- Save draft is `secondary` — "Save"
+- Unsaved changes: show a `Note variant="warning"` below the Header, not a blocking modal
+- Discard changes: show a confirmation Modal before navigating away
+
+---
+
+## 3. Settings Page
+
+Configuration of a feature, app, or space setting.
+
+```
+Header
+ title: "[Feature] settings"
+ actions: [optional "Save changes" primary button]
+
+Content area
+ ├── Optional: Tabs (if settings have distinct categories)
+ ├── Sections with Heading separator
+ │ ├── FormControl fields
+ │ └── Optional Note for context
+ └── Danger zone section (at bottom, with negative Note)
+ └── Destructive actions (delete, reset, disable)
+```
+
+**Rules:**
+- Group related settings under a `Heading` or `SectionHeading` with a `gray/200` divider
+- Danger zone (destructive actions) always goes at the very bottom, separated visually
+- Destructive actions in danger zone: `negative` variant `Button`, always trigger a confirmation Modal
+- Inline save: show `Notification.success` after saving — don't navigate away
+- Auto-save: show a subtle "Saving…" Spinner + "Saved" text near the field or in a status area
+
+---
+
+## 4. Empty State
+
+Used when a section has no content yet — first-time experience, no search results, or after clearing filters.
+
+```
+Centered container (vertical + horizontal center of content area)
+ ├── Icon (optional): 48px, gray/400, relevant to the section
+ ├── Heading: "No [items] yet"
+ ├── Body: one sentence describing purpose or next step
+ └── Primary action: Button variant="primary" or TextLink
+```
+
+**Heading formulas:**
+- First-time: "No [content types] yet"
+- No search results: `No results for "[query]"`
+- Permission-restricted: "You don't have access to [section]"
+
+**Body formulas:**
+- First-time: "[Section] lets you [what it does]. Start by [first action]."
+- No results: "Try a different search term or [adjust filters]."
+- Error: "Something went wrong while loading [items]. [Retry link]."
+
+**Rules:**
+- Never show an empty state while data is still loading — show Skeleton instead
+- Never show a generic "No data" — always be specific about what's missing and what to do
+- Keep body copy to one sentence
+- Only one action in an empty state — the most obvious next step
+
+---
+
+## 5. Error State
+
+When a page or section fails to load or an operation fails unrecoverably.
+
+```
+Centered container
+ ├── Icon: WarningIcon or CloseCircleIcon, 48px, red/600
+ ├── Heading: "Couldn't load [section]"
+ ├── Body: brief explanation + what the user can do
+ └── Actions: "Try again" (primary) + optional "Go back" (secondary)
+```
+
+**Rules:**
+- "Couldn't load" not "Error 500" or technical codes — humanize
+- Always offer a clear recovery action ("Try again", "Go back", "Contact support")
+- Log technical details to console; show human language in the UI
+- For partial failures (one section fails, rest loads): use `Note variant="negative"` inline, not a full-page error
+
+---
+
+## 6. Confirmation Dialog (Destructive Action)
+
+Triggered before any irreversible action.
+
+```
+Modal size="small"
+ Header: "[Delete/Remove/Archive] [item type]"
+ Content:
+ Paragraph: "This will permanently [consequence]. This cannot be undone."
+ Optional: list of what will be affected
+ Controls:
+ Button variant="negative": "[Delete/Remove/Archive] [item type]"
+ Button variant="secondary": "Never mind"
+```
+
+**Rules:**
+- Title and confirm button always match: same verb + noun
+- Confirm button is `negative` variant — always
+- Cancel is always "Never mind" — never "Cancel", "No", or "Go back"
+- Body: state the consequence specifically — never vague ("This action cannot be undone")
+- `shouldCloseOnOverlayClick={false}` for destructive modals — user must make an explicit choice
+- Confirm button shows `isLoading` while the action is in flight
+
+---
+
+## 7. Multi-Step Flow / Wizard
+
+Onboarding, content type creation, or any guided multi-step process.
+
+```
+Layout variant="narrow" or Modal size="large" / "fullscreen"
+ ├── Step indicator (top, horizontal) — "Step 2 of 4"
+ ├── Header: step title
+ ├── Content: step-specific form or information
+ └── Footer controls:
+ ├── Left: "Back" (secondary, or hidden on step 1)
+ └── Right: "Continue" (primary) or "Finish" (positive, last step)
+```
+
+**Rules:**
+- "Back" on left, "Continue" on right — never reversed
+- Final step confirm button: `positive` variant — "Create [thing]" or "Finish setup"
+- Show a progress indicator (step count or progress bar) — never leave users wondering how long the flow is
+- Validate before allowing "Continue" — show `ValidationMessage` inline, not a blocking alert
+- Allow exiting the flow — show discard confirmation if data has been entered
+
+---
+
+## 8. Dashboard / Overview Page
+
+High-level summary of a space or feature — metrics, recent activity, quick actions.
+
+```
+Header
+ title: "Overview" or space/project name
+ actions: optional primary CTA
+
+Content area
+ ├── Row of metric Cards (3–4 columns)
+ ├── Recent activity list or EntityList
+ └── Quick action Cards or links
+```
+
+**Rules:**
+- Cards in metric rows: equal width, consistent padding `default`
+- No more than 4 metric cards in a row before wrapping or reorganizing into a different layout
+- "Recent activity" shows last 5–10 items — link to full list
+- Loading: show `Skeleton.Container` per card, not a full-page spinner
+- Empty state per section independently — not one global empty state for the whole dashboard
+
+---
+
+## 9. Form Page (Standalone)
+
+A dedicated page for a single form — app configuration, user preferences, invite flow.
+
+```
+Layout variant="narrow"
+ Header: form title
+ Content:
+ Stack of FormControl fields (spacing="spacingM")
+ Optional: Note for context or instructions
+ Optional: Accordion for advanced/optional sections
+ Footer or inline:
+ Button variant="primary": "Save" or "Submit"
+ Optional Button variant="secondary": "Cancel" (navigates back)
+```
+
+**Rules:**
+- Use `Layout variant="narrow"` to keep forms readable — never stretch a form to full width
+- Group related fields under a `Heading` — don't list 15 fields in one undifferentiated block
+- Required fields: mark with `isRequired` on `FormControl` — the asterisk appears automatically
+- Validation: inline, on blur or on submit — never a separate error summary page
+- On success: `Notification.success` + navigate to the result, or stay on form with a success state
+
+---
+
+## Navigation Hierarchy
+
+When building multi-level navigation:
+
+| Level | Pattern |
+|---|---|
+| Top-level sections | Left sidebar `M1NavigationSidebarMain` sections |
+| Sub-sections within a section | Tabs in the content area |
+| Drill-down to a single item | Header with Breadcrumb + back button |
+| Related views on a single item | Tabs in the detail page content area |
+
+**Never use modals as navigation.** A modal should complete a task, not replace a page.
+**Never use Tabs at the sidebar level** — sidebar items navigate between sections, not tabs.
From 7286e2b21715214cf5644110f673b06de94804b7 Mon Sep 17 00:00:00 2001
From: damann <67584870+damann@users.noreply.github.com>
Date: Mon, 13 Apr 2026 16:28:47 +0200
Subject: [PATCH 05/13] small formatting change in SKILL
Add description for Forma 36 UI design system.
---
skills/SKILL.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/skills/SKILL.md b/skills/SKILL.md
index 9b4628a783..f918251b35 100644
--- a/skills/SKILL.md
+++ b/skills/SKILL.md
@@ -1,3 +1,4 @@
+---
name: forma36
description: Build UI using Forma 36, Contentful's design system. Use when the user references Forma 36, f36, Contentful components, or wants to build a Contentful app UI — including prototypes, screens, and interface explorations.
---
From 0af84aa964fcfeb360ee34fe49a2a4d10aef1ad5 Mon Sep 17 00:00:00 2001
From: damann <67584870+damann@users.noreply.github.com>
Date: Mon, 13 Apr 2026 16:30:30 +0200
Subject: [PATCH 06/13] Rename ui-patterns.md to screen-patterns.md
---
skills/{ui-patterns.md => screen-patterns.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename skills/{ui-patterns.md => screen-patterns.md} (100%)
diff --git a/skills/ui-patterns.md b/skills/screen-patterns.md
similarity index 100%
rename from skills/ui-patterns.md
rename to skills/screen-patterns.md
From 83fcb724bc5fbeec90b220c7be3c12efd59da319 Mon Sep 17 00:00:00 2001
From: damann <67584870+damann@users.noreply.github.com>
Date: Mon, 13 Apr 2026 16:30:50 +0200
Subject: [PATCH 07/13] Rename UI Patterns to Screen Patterns
Updated the title to reflect 'Screen Patterns' instead of 'UI Patterns'.
---
skills/screen-patterns.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/skills/screen-patterns.md b/skills/screen-patterns.md
index e2a89d6d71..7d3c57e189 100644
--- a/skills/screen-patterns.md
+++ b/skills/screen-patterns.md
@@ -1,4 +1,4 @@
-# Forma 36 — UI Patterns
+# Forma 36 — Screen Patterns
Named, reusable patterns for the most common Contentful UI screens. When a design calls for one of these, use this as the structural template — not a blank canvas.
From 39f680ef06308b195b85417c35578422385a3fe4 Mon Sep 17 00:00:00 2001
From: toniwowtscherk
Date: Fri, 17 Apr 2026 09:56:29 +0200
Subject: [PATCH 08/13] Restructure Forma 36 skill into granular Make Kit
format
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replace the monolithic SKILL.md/tokens.md/screen-patterns.md with a
structured guidelines/ directory following Figma Make Kit best practices
(many short, focused files over few large ones).
New structure:
- guidelines/tokens/ — color, typography, spacing, effects
- guidelines/components/ — layout, buttons, forms, feedback, navigation,
data-display, content-media, utility
- guidelines/composition/ — base shell, 9 screen patterns
- guidelines/foundations/ — writing, icons, accessibility
- examples/ — 6 TSX reference implementations (list, detail, settings,
confirmation, form, error)
SKILL.md now routes two audiences:
- Path A (designers): composition-first, visual-language focused
- Path B (developers): setup-first, prop-precision focused
Co-Authored-By: Claude Opus 4.6
---
skills/SKILL.md | 946 +++---------------
skills/examples/confirmation-dialog.tsx | 50 +
skills/examples/detail-page.tsx | 84 ++
skills/examples/error-state.tsx | 37 +
skills/examples/form-page.tsx | 108 ++
skills/examples/list-page.tsx | 146 +++
skills/examples/settings-page.tsx | 84 ++
skills/guidelines/components/buttons.md | 87 ++
skills/guidelines/components/content-media.md | 65 ++
skills/guidelines/components/data-display.md | 145 +++
skills/guidelines/components/feedback.md | 96 ++
skills/guidelines/components/forms.md | 156 +++
skills/guidelines/components/layout.md | 139 +++
skills/guidelines/components/navigation.md | 133 +++
skills/guidelines/components/overview.md | 60 ++
skills/guidelines/components/utility.md | 88 ++
skills/guidelines/composition/base-shell.md | 66 ++
skills/guidelines/composition/overview.md | 14 +
.../guidelines/composition/screen-patterns.md | 210 ++++
.../guidelines/foundations/accessibility.md | 42 +
skills/guidelines/foundations/icons.md | 47 +
skills/guidelines/foundations/overview.md | 9 +
skills/guidelines/foundations/writing.md | 86 ++
skills/guidelines/overview.md | 36 +
skills/guidelines/setup.md | 59 ++
skills/guidelines/tokens/color.md | 117 +++
skills/guidelines/tokens/effects.md | 72 ++
skills/guidelines/tokens/overview.md | 25 +
skills/guidelines/tokens/spacing.md | 54 +
skills/guidelines/tokens/typography.md | 79 ++
skills/screen-patterns.md | 255 -----
skills/tokens.md | 218 ----
32 files changed, 2517 insertions(+), 1296 deletions(-)
create mode 100644 skills/examples/confirmation-dialog.tsx
create mode 100644 skills/examples/detail-page.tsx
create mode 100644 skills/examples/error-state.tsx
create mode 100644 skills/examples/form-page.tsx
create mode 100644 skills/examples/list-page.tsx
create mode 100644 skills/examples/settings-page.tsx
create mode 100644 skills/guidelines/components/buttons.md
create mode 100644 skills/guidelines/components/content-media.md
create mode 100644 skills/guidelines/components/data-display.md
create mode 100644 skills/guidelines/components/feedback.md
create mode 100644 skills/guidelines/components/forms.md
create mode 100644 skills/guidelines/components/layout.md
create mode 100644 skills/guidelines/components/navigation.md
create mode 100644 skills/guidelines/components/overview.md
create mode 100644 skills/guidelines/components/utility.md
create mode 100644 skills/guidelines/composition/base-shell.md
create mode 100644 skills/guidelines/composition/overview.md
create mode 100644 skills/guidelines/composition/screen-patterns.md
create mode 100644 skills/guidelines/foundations/accessibility.md
create mode 100644 skills/guidelines/foundations/icons.md
create mode 100644 skills/guidelines/foundations/overview.md
create mode 100644 skills/guidelines/foundations/writing.md
create mode 100644 skills/guidelines/overview.md
create mode 100644 skills/guidelines/setup.md
create mode 100644 skills/guidelines/tokens/color.md
create mode 100644 skills/guidelines/tokens/effects.md
create mode 100644 skills/guidelines/tokens/overview.md
create mode 100644 skills/guidelines/tokens/spacing.md
create mode 100644 skills/guidelines/tokens/typography.md
delete mode 100644 skills/screen-patterns.md
delete mode 100644 skills/tokens.md
diff --git a/skills/SKILL.md b/skills/SKILL.md
index f918251b35..f0a6eac0b6 100644
--- a/skills/SKILL.md
+++ b/skills/SKILL.md
@@ -1,875 +1,175 @@
---
name: forma36
-description: Build UI using Forma 36, Contentful's design system. Use when the user references Forma 36, f36, Contentful components, or wants to build a Contentful app UI — including prototypes, screens, and interface explorations.
+description: >
+ Forma 36 — Contentful's design system. Tokens, components, composition patterns,
+ writing rules, and reference implementations for AI agents building Contentful UI.
+ Use when the user references Forma 36, f36, Contentful components, or wants to
+ build a Contentful app UI.
---
# Forma 36 — Contentful Design System
-Forma 36 is the design language of Contentful. Every prototype, screen, and component built for Contentful must look and feel like it belongs in the Contentful product. This skill ensures that.
+Forma 36 is the design language of Contentful. Every screen, component, and interaction built for Contentful must look and feel like it belongs in the product — calm, trustworthy, and focused on the user's task.
-The goal is not to use the right component name — it's to produce UI that is indistinguishable from the real Contentful interface.
-
-## Scope, Authority, and Rule Interpretation
-
-This document is the primary operational specification for generating UI with Forma 36.
-
-Use it to determine:
-- which UI structures are allowed
-- which components and patterns to use
-- which visual and writing rules are mandatory
-- which implementations are forbidden
-
-This is a normative specification. Outputs must follow explicit rules. Philosophy sections explain intent but do not override rules or permit invention.
-
----
-
-## Rule Types
-
-All guidance is expressed using the following rule types:
-
-- **Required** — mandatory in every applicable case. Treat as pass/fail.
-- **Forbidden** — never allowed. If present, the UI is incorrect.
-- **Preferred** — default choice unless a more specific rule overrides it.
-- **Context-dependent** — mandatory only when the stated condition is true.
-
-### Rule precedence
-
-Apply rules in this order:
-1. Forbidden
-2. Required
-3. Context-dependent (when condition is true)
-4. Preferred
-5. Philosophy and explanatory text
-
-When applying rules:
-- apply conditional rules only when their condition is true
-- when rules conflict, the more specific rule wins
-- Forbidden rules always take precedence
-
-Only explicit rules, named components, defined patterns, and tokenized values are normative.
-
----
-
-## Supporting Files
-
-Supporting files extend this specification with domain-specific detail.
-
-Apply them as follows:
-- use this document first to determine structure and rule intent
-- use the relevant supporting file for detailed decisions
-- when rules do not conflict, apply both
-- when rules conflict, the more specific rule wins
-
-This skill is split across four reference files. Read the relevant one for the task at hand:
-
-| File | What's in it |
-|---|---|
-| `tokens.md` | Complete token reference — all colors, spacing, typography, shadows, border radius, z-index |
-| `components.md` | Every component: usage intent, key props, code examples, rules |
-| `screen-patterns.md` | Named layout patterns — list page, detail page, empty state, error state, confirmation dialog, wizard, dashboard |
-| `brand-guidelines.md` | Voice, tone, writing rules, icon style, photography guidance |
+**Docs:** https://f36.contentful.com
+**Source:** https://github.com/contentful/forma-36
---
-## Generation Constraints
-
-**Forbidden**
-- Do not invent new components.
-- Do not invent new layout patterns.
-- Do not invent new visual styles.
-- Do not invent new interaction models.
-- Do not invent new token values.
-- Do not substitute a custom solution when a defined component, primitive, or pattern exists.
-
-**Preferred**
-- Use defined Forma 36 components.
-- Use layout primitives: Box, Flex, Stack, Grid.
-- Use tokenized spacing, radius, color, and typography.
-- Use named patterns from screen-patterns.md when applicable.
-
-**Context-dependent**
-- If required information is missing, incomplete, or unspecified:
- - do not guess beyond the defined system
- - compose solutions using available primitives and defined components
- - apply existing rule types and precedence
- - prefer standard patterns over custom implementations
-
-## Fallback behavior
-
-These rules define how to proceed when required information is missing, incomplete, ambiguous, or in conflict. Apply them with the rule precedence defined in this document.
-
-### Missing component definition
-
-**Required**
-- If a needed component is not defined in this document or the supporting files, do not invent a new component.
-- Compose the UI using defined components and layout primitives only.
-- Prefer the simplest composition that satisfies the requirement using Box, Flex, Stack, Grid, and defined Forma 36 components.
+## Choose your path
-**Forbidden**
-- Do not create a custom component, custom variant, or custom API to fill the gap.
+This skill serves two audiences. Pick the one that matches your task, then follow its reading order.
-### Missing layout or pattern
+### Path A — Designer prototyping
-**Required**
-- If a needed layout or named pattern is not defined, use the standard application shell defined in this document.
-- Compose the screen from the existing header, content area, sidebar, and layout primitives only.
-- Prefer a standard composition over a custom layout.
-
-**Forbidden**
-- Do not invent a new page pattern, navigation pattern, or layout structure.
-
-### Missing interaction or state behavior
-
-**Required**
-- If a component-specific interaction rule is missing, use the global Interaction states section as the source of truth.
-- Keep behavior minimal and within the defined component model.
-
-**Context-dependent**
-- If a state is not applicable to a component, omit it.
-
-**Forbidden**
-- Do not invent a new interaction model, custom animation, or undefined state behavior.
-
-### Missing accessibility rule
-
-**Required**
-- If a component-specific accessibility rule is missing, apply the explicit accessibility rules already defined in this document for labels, keyboard operation, focus visibility, heading structure, state communication, and text alternatives.
-- Prefer native semantics and documented component APIs over custom accessibility implementations.
-
-**Forbidden**
-- Do not assume undocumented accessibility behavior for a component.
-- Do not remove an accessibility requirement because a component-specific rule is missing.
-
-### Conflicting rules
-
-**Required**
-- Apply rule precedence defined in this document.
-- When rules conflict, the more specific rule wins.
-- When a global rule and a component-specific rule conflict, use the component-specific rule if it does not violate a Forbidden rule.
-
-**Forbidden**
-- Do not merge conflicting rules into a compromise implementation.
-- Do not ignore a Forbidden rule because another rule is less strict.
-
-### Ambiguous or underspecified requirements
-
-**Required**
-- If the requirement is underspecified, select the simplest valid option that is explicitly supported by this document.
-- Prefer defined components over composition, and composition over omission, when both remain within documented rules.
-- If a requirement cannot be satisfied without inventing a component, pattern, behavior, or rule, omit the unsupported part.
-
-**Forbidden**
-- Do not approximate with a “close enough” custom solution.
-- Do not infer undocumented product behavior from philosophy text, examples, or visual style alone.
-
-### Safe default rules
-
-**Required**
-- Stay within defined tokens, defined components, defined patterns, and defined state rules.
-- Prefer standard, lower-complexity compositions over dense or highly customized layouts.
-- Preserve existing explicit constraints even when information is missing elsewhere.
-
-**Forbidden**
-- Do not add decorative UI, auxiliary controls, or optional interactions that are not required by the specification.
-
-## Design Philosophy
-
-This section is non-normative. It explains intent. It does not create requirements, override explicit rules, or permit invention. Use it only to interpret explicit rules elsewhere in this document.
-
-Forma 36 is built on one idea: software should feel calm, trustworthy, and focused.
-
-Supporting principles:
-
-1. **Clarity over cleverness**
-Prioritize task clarity over decorative or novel presentation.
-
-2. **Restraint as a design decision**
-Prefer lower visual intensity, clear hierarchy, and semantic use of color.
-
-3. **Consistency builds trust**
-Prefer existing patterns, consistent spacing, consistent component behavior, and consistent language.
-
-4. **Accessibility is non-negotiable**
-Apply the explicit accessibility, keyboard, focus, labeling, and state communication rules defined elsewhere in this document.
-
----
+You are designing a screen, vibe-coding a prototype, or exploring a UI concept in Figma Make. You care about how it *looks and feels* — layout, visual language, screen structure, copy.
-## Visual Language
-
-### Color
-
-| Role | Color | Hex | Use |
-|---|---|---|---|
-| Primary action | Blue 600 | #0059C8 | Primary interactive action |
-| Success / published | Green 600 | #006D23 | Positive outcomes, published state |
-| Danger / error | Red 600 | #BD002A | Destructive actions, errors |
-| Warning / draft | Orange 500 | #CC4500 | Caution, unpublished or draft state |
-| Premium / new | Purple 600 | #6B3DB5 | Paid features |
-| UI surface | Gray 100 | #F7F9FA | Page backgrounds |
-| Panel surface | White | #FFFFFF | Cards, sidebars, modals |
-| Default text | Gray 900 | #111B2B | Body copy, labels |
-| Secondary text | Gray 700 | #414D63 | Section headings, metadata |
-| Muted text | Gray 600 | #536171 | Hints, inactive labels |
-| Borders / dividers | Gray 200 | #E7EBEE | Dividers between sections |
-
-**Supported shade sets**
-- blue100 to blue900
-- gray100 to gray900
-- green100 to green900
-- orange100 to orange900
-- purple100 to purple900
-- red100 to red900
-- yellow100 to yellow900
-
-**Required**
-- Use only supported shade sets and defined color roles.
-- Pair semantic color with text or icon when the color communicates meaning.
-- Use Blue 600 only for primary interactive actions.
-- Use Purple 600 only for paid features.
-- Use blue/100 (#E8F5FF) as the active navigation background.
-- Use black text darker than gray-800 only on shades 100 to 400.
-- Use white text only on shades 500 to 900.
-- For non-yellow same-color foreground/background combinations, use only:
- - 600 on 100
- - 600 on 200
- - 700 on 300
- - 300 on 900
- - 500 on white
- - 500 on gray100
-- For yellow same-color foreground/background combinations, use only:
- - 800 on 100
- - 800 on 200
- - 900 on 300
- - 300 on 900
- - 800 on white
- - 800 on gray100
-
-**Forbidden**
-- Do not use color as decoration.
-- Do not rely on color alone to communicate meaning.
-- Do not use blue for non-interactive decoration.
-- Do not use a blue text color for active navigation items.
-- Do not use unsupported foreground/background shade combinations.
-- Do not use non-yellow same-color combinations outside the allowed set.
-- Do not use yellow same-color combinations outside the allowed yellow set.
-- Do not use yellow when another defined semantic role matches the meaning.
-
-### Typography
-
-| Role | Size | Weight | Use |
-|---|---|---|---|
-| Display | 1.5rem+ | 600 | Page titles, hero text |
-| Section heading | 0.75rem | 600 | Nav group labels, table headers |
-| Body (default) | 0.875rem | 400 | Interface copy |
-| Body medium | 0.875rem | 500 | Emphasized labels |
-| Small / caption | 0.75rem | 400 | Metadata, hints |
-
-**Required**
-- Use Geist as the typeface.
-- Import from https://cdn.f36.contentful.com.
-- Use fallback fonts -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif.
-- Use font-feature-settings: 'ss05' 1 on all Geist text.
-- Use line height 1.5 by default.
-- Use line height 1.25 only for nav items and badges.
-- Use letter spacing 0 for all sizes.
-- Use sentence case for labels.
-
-**Forbidden**
-- Do not mix more than two font weights on a single screen.
-- Do not use Title Case for labels.
-- Do not use ALL CAPS for labels.
-
-### Spacing
-
-| Token | Value | Common use |
-|---|---|---|
-| 2xs | 4px | Icon gaps, tight groupings |
-| xs | 8px | Between related items |
-| s | 12px | Component internal padding |
-| m | 16px | Standard padding |
-| l | 24px | Section padding |
-| xl | 32px | Large section gaps |
-| 2xl | 48px | Page-level separation |
-
-**Required**
-- Use only the defined spacing tokens.
-- Manage external spacing with layout wrappers: Flex, Stack, Box.
-- Use token names on layout components: spacingXs, spacingS, spacingM, etc.
-
-**Forbidden**
-- Do not use arbitrary pixel values for spacing.
-- Do not assign external spacing directly to components.
-
-### Shadows
-
-| Name | Value | Use |
-|---|---|---|
-| Default | 0 0 0 1px rgba(25,37,50,.10), 0 3px 7px -3px rgba(25,37,50,.10), 0 6px 12px -2px rgba(25,37,50,.10) | Panels, sidebar, content area |
-| Button | 0 1px 0 rgba(17,27,43,.05) | Secondary buttons |
+**Figma Make Kit:** https://www.figma.com/make/jcC82kTLl5yz6W9Mk1LJF8/Forma-36-Kit
-**Required**
-- Use Shadow/Default for panels, sidebar, and content area.
-- Use Shadow/Button for secondary buttons.
+**Reading order:**
+1. `guidelines/overview.md` — Design philosophy and critical rules
+2. `guidelines/composition/base-shell.md` — The app chrome you must always use
+3. `guidelines/composition/screen-patterns.md` — Pick the pattern closest to your screen
+4. `guidelines/foundations/writing.md` — Get the copy right (sentence case, verb-first labels, "Never mind")
+5. `guidelines/tokens/color.md` — Understand what each color means
+6. `guidelines/tokens/typography.md` — Type roles and the Geist setup
+7. `guidelines/tokens/spacing.md` — The spacing scale
+8. `guidelines/components/overview.md` — Skim the decision tree to pick the right components
+9. `examples/` — Start from the example closest to your screen and adapt it
-**Forbidden**
-- Do not create custom shadows.
-- Do not create glows.
+**Focus on:** Screen patterns, visual hierarchy, writing rules, color meaning.
+**Skim:** Individual component prop tables — reference them only when you need a specific detail.
+**Use examples as:** Starting templates to fork and adapt.
-### Borders & Radius
+### Path B — Developer implementing
-| Element | Radius |
-|---|---|
-| Panels, cards | 12px (top corners only for flush-to-edge panels) |
-| Buttons, inputs, nav items | 6px |
-| Small chips, badges | 4px |
-| Avatars | 100px (pill) |
-
-**Required**
-- Use only the defined radius values.
-- Use 12px for panels and cards.
-- Use top corners only for flush-to-edge panels.
-- Use 6px for buttons, inputs, and nav items.
-- Use 4px for small chips and badges.
-- Use 100px for avatars.
-
-**Forbidden**
-- Do not use border radius values outside 4px, 6px, 12px, and 100px.
+You are writing production code, building a Contentful app, or implementing a design spec. You need exact prop names, token values, correct import paths, and API precision.
----
+**Reading order:**
+1. `guidelines/overview.md` — Critical rules (memorize these)
+2. `guidelines/setup.md` — Dependencies, CSS import, font loading
+3. `guidelines/tokens/` — Read all token files. Every value you use must come from here.
+4. `guidelines/components/overview.md` — Decision tree, then read the specific component file you need
+5. `guidelines/components/.md` — Full prop tables, code examples, constraints
+6. `guidelines/composition/screen-patterns.md` — Structural template for the page type you're building
+7. `guidelines/foundations/accessibility.md` — ARIA labels, focus management, contrast
+8. `examples/` — Verify your patterns match the reference implementations
-## Base Layout
-
-**Required**
-- Every screen uses this exact shell.
-- Do not invent alternative chrome.
-
-Figma reference: `https://www.figma.com/design/CHqkCTVTpvkqDNXtoAR5cE/Experiences-Library?node-id=6764-6490`
-
-┌──────────────────────────────────────────────────────┐
-│ Top bar (52px, gray-100 background) │
-├───────────────┬──────────────────────────────────────┤
-│ │ Content area │
-│ Sidebar │ ┌────────────────────────────────┐ │
-│ (240px) │ │ Header (title + actions) │ │
-│ │ ├────────────────────────────────┤ │
-│ │ │ Page content │ │
-│ │ └────────────────────────────────┘ │
-└───────────────┴──────────────────────────────────────┘
-[8px agent strip]
-
-### Top bar
-
-**Required**
-- Height: `52px`.
-- Background: `gray/100`.
-- Horizontal padding: `24px` left, `64px` right.
-- The `64px` right gap is reserved for the agent strip.
-- Left content order: Contentful `"C"` logo → Space/Environment switcher pill.
-- Right content order: Search icon → Help icon → User avatar (`24px`).
-- Environment switcher background: `green/100`.
-- Environment switcher border: `green/400`.
-- Environment switcher text: `"Space > Production"` in `green/700`.
-- Environment switcher includes a `green-300` right-edge indicator strip.
-- Environment switcher includes a rocket icon.
-
-**Forbidden**
-- Do not change the top bar height.
-- Keep the top bar content order consistent with the defined structure.
-- Do not use an alternative top bar structure.
-
-### Sidebar
-
-#### Structure
-
-**Required**
-- Sidebar is present in the application shell.
-- Sidebar includes a collapse button.
-- Collapse button is a transparent `IconButton` with `CaretLeft` icon.
-- Collapse button position: bottom-right of sidebar.
-- Sidebar includes all defined sections, items, icons, and states.
-
-**Forbidden**
-- Do not stub the sidebar.
-- Do not simplify the sidebar.
-- Do not remove defined sections, items, icons, or states.
-
-#### Dimensions
-
-**Required**
-- Width: `240px`.
-- Background: `white`.
-- Shadow: `Shadow/Default`.
-- Top corners radius: `12px`.
-- Padding: `pt-12px pb-8px px-4px`.
-- Inner menu padding: `px-12px`.
-- Gap between sections: `12px`.
-- Nav item height: `32px`.
-- Nav item padding: `px-12px py-6px`.
-- Nav item gap: `8px`.
-- Nav item border radius: `4px`.
-- Section title size: `12px`.
-- Section title weight: `600`.
-- Section title color: `gray/700`.
-- Section title height: `28px`.
-- Section title left padding: `pl-12px`.
-
-#### Content
-
-**Required**
-- Sections are separated by `gray/200` bottom border.
-- Sidebar sections and items:
- 1. **Setup** — Design System *(active by default)*, Content Model
- 2. **Creation** — Experiences, Content
- 3. **Optimization** — Optimizations, Analytics
- 4. **Platform Configuration** — Collections, AI & Automations, Taxonomy, Apps
- 5. *(no title)* — Settings
-
-#### States
-
-**Required**
-- See Interaction states.
-- Nav item content: Icon (`16px`, duotone variant for active) + label (body regular, `gray/900`) + optional caret right.
-- Active state background: `blue/100` (`#E8F5FF`).
-- Default state background: transparent.
-- Hover state background: `gray/100`.
-
-#### Constraints
-
-**Required**
-- Implement the sidebar faithfully.
-- Replicate all sections, items, icons, and states exactly.
-
-**Forbidden**
-- Do not modify sidebar structure outside the defined shell.
-- Do not invent additional sidebar patterns.
-
-### Content area
-
-**Required**
-- Content area fills the remaining width.
-- Background: `white`.
-- Shadow: `Shadow/Default`.
-- Top corners radius: `12px`.
-- Padding: `pt-12px px-24px`.
-- `Header` is always present at the top of the content area.
-- Use `Title` variant for top-level pages.
-- Use `Breadcrumb` variant for subpages.
-- Page content is placed below the `Header`.
-
-**Context-dependent**
-- Set `dividerLine={true}` when the content area has a right sidebar.
-
-**Forbidden**
-- Do not omit the `Header`.
-- Do not place page content above the `Header`.
+**Focus on:** Token names, prop signatures, component rules, accessibility requirements, code conventions.
+**Skim:** Composition overview and writing guidelines — reference them for structure and copy decisions.
+**Use examples as:** API reference and correctness checks.
---
-## Layout rules
-
-### Layout Primitives
-
-**Required**
-- Use layout primitives to compose all layout structure outside the application shell.
-- Use only Flex, Stack, Box, and Grid for layout composition.
-- Use tokenized spacing values on layout primitives: spacingXs, spacingS, spacingM, spacingL, spacingXl, spacing2Xl.
-- Use layout primitives to control external spacing between components.
-- Keep component external spacing outside the component itself.
-- Keep layout composition inside the existing application shell defined in this document.
-
-**Preferred**
-- Use Stack for one-directional vertical grouping.
-- Use Flex for one-directional layouts that require explicit alignment, justification, or wrapping behavior.
-- Use Grid for two-dimensional layouts with rows and columns.
-- Use Box for single-region containment, padding, margin, width, or other block-level layout control.
-- Use the simplest layout primitive that satisfies the requirement.
-- Prefer Stack over Flex when only directional spacing is required.
-- Prefer Flex over Grid when the layout is one-dimensional.
-
-**Required**
-- Compose layouts from primitives first, then place defined components inside them.
-- Use nested primitives when one primitive cannot express the required structure.
-- Keep DOM order aligned with visual order.
-- Keep page-level spacing, section spacing, and inter-component spacing on layout primitives, not on child components.
-- Use existing shell regions as fixed boundaries: top bar, sidebar, content area, header, page content.
-- Keep the Header at the top of the content area.
-- Place unrestricted page content below the Header.
-
-**Context-dependent**
-- When content requires explicit columns or row/column placement, use Grid.
-- When content is arranged in a single vertical flow, use Stack.
-- When items must be aligned horizontally or distributed along one axis, use Flex.
-- When only padding, margin, width, or a single container wrapper is needed, use Box.
-- When spacing between elements is required, use spacing tokens only.
-- When a right sidebar is present in the content area, follow the existing Header rule for dividerLine={true}.
-- If a layout requirement is underspecified, use the simplest valid composition built from Box, Flex, Stack, and Grid.
-- If a named pattern is missing, compose the layout from the standard application shell and layout primitives only.
-
-**Forbidden**
-- Do not invent new layout primitives.
-- Do not invent new layout patterns outside the application shell and named patterns defined elsewhere in this document.
-- Do not use arbitrary pixel values for layout spacing.
-- Do not use components as layout substitutes when a layout primitive is sufficient.
-- Do not assign external spacing directly to components.
-- Do not use Grid for one-dimensional layouts.
-- Do not use Flex or Stack to approximate a two-dimensional grid layout.
-- Do not break the shell structure defined in Base Layout.
-- Do not reorder DOM structure to create a visual layout that changes reading or interaction order.
+## Critical rules
----
+These apply to both paths. Do not override them.
-## Component rules
-
-### Buttons
-
-**Required**
-- Use `Button` for button actions.
-- Use `Primary` for the single most important action in a context. Maximum: one per context.
-- Use `Secondary` for supporting actions.
-- Use `Positive` for constructive actions: create, publish, add.
-- Use `Negative` for destructive or irreversible actions: delete, remove, archive.
-- Use `Transparent` only on light backgrounds.
-- Use native button semantics for button actions.
-- Icon-only actions must use `IconButton` or `ToggleButton` with a descriptive `aria-label`.
-- Button groups must expose an `aria-label`.
-
-**Context-dependent**
-- Use `small` for toolbars.
-- Use `medium` as the default size.
-- Use `large` only for hero CTAs.
-- If a button is marked disabled through a custom button element or `role=button`, click and keydown handlers must not fire.
-- For state behavior, see **Interaction states**.
-- For loading behavior, see **Interaction & State rules**.
-
-**Forbidden**
-- Do not use more than one `Primary` button in the same context.
-
-### Forms
-
-**Required**
-- Every input must live inside a `FormControl`.
-- Structure is always: `Label → Input → Help text → Validation message`.
-- `FormControl.Label` is required.
-- `FormControl.HelpText` is for guidance that always shows.
-- Pass `isRequired`, `isInvalid`, and `isDisabled` to `FormControl`, not to the input directly.
-- Required form elements must be marked programmatically with `isRequired`, not only visually.
-- Help text and validation messages must stay inside the same `FormControl`.
-- `TextInput` and `Textarea` with separate labels must expose `id` for `htmlFor`.
-- Controls used in form submission must expose `name` when submission depends on it.
-- Choose the input type as follows:
- - Short free text → `TextInput`
- - Long free text → `Textarea`
- - Pick one from a short list (`≤8` options) → `Select`
- - Pick one from a long list → `Autocomplete`
- - Toggle one option → `Checkbox`
- - Pick one from a mutually exclusive group → `Radio.Group`
-
-**Context-dependent**
-- `FormControl.ValidationMessage` is for errors and appears only when invalid. See **Feedback selection rules**.
-- If there is no visible label, provide `aria-label` or `aria-labelledby` as supported by the control.
-- Use `Checkbox.Group` or an equivalent fieldset/legend structure for related checkbox options.
-- Use `Radio.Group` to group related radio buttons and provide a group label.
-- If a visible legend is not possible for checkbox or radio groups, use `aria-labelledby`.
-- `Switch` labels must describe the action being controlled.
-- If a visible label is not present for `Select`, provide `aria-label`.
-- Use `Datepicker` inside `FormControl` when label, help text, or validation is needed.
-- Icon-only `Datepicker` trigger buttons must provide a screen-reader label.
-- Disabled or invalid date states must be communicated in text, not only visually.
-- In `Multiselect`, if a visible label is not present, provide an `aria-label`.
-- For state behavior, see **Interaction states**.
-
-**Forbidden**
-- Do not use placeholder text as a replacement for a label.
-
-### Feedback & Status
-
-**Required**
-- Use `Notification` for immediate feedback after a user action.
-- `Notification` dismisses after 6 seconds.
-- `Notification.success('Entry published')` uses active voice and present tense.
-- `Notification.error(...)` must be specific and actionable.
-- Use `Note` for proactive context the user needs before acting.
-- Place `Note` near the relevant content, not at the top of the page generically.
-- Badge labels and status indicators stay to one word.
-- Use `EntityStatusBadge` for Contentful entity status.
-
-**Context-dependent**
-- `Note` variants: neutral, primary, positive, warning, negative, premium.
-- Badge status mapping:
- - positive = Published
- - warning = Draft
- - negative = Deleted
-- For selection logic, see **Feedback selection rules**.
-
-**Forbidden**
-- Do not use `Notification` for persistent context.
-- Do not construct a raw `Badge` for Contentful entity status.
-
-### Navigation & Overlays
-
-**Required**
-- For `Modal` usage, see **Feedback selection rules**.
-- `Modal` content must include a clear title and description.
-- Use `Menu` for actions and navigation choices triggered by a button.
-- `Tabs` navigate between sections of related content.
-- Maximum tabs: `6`.
-- Tab labels must accurately describe the panel content.
-- Provide a unique `panelId` for each tab-panel relationship.
-- `Tooltip` is supplementary, non-critical context.
-- Tooltip content must be short.
-- Always include a unique `id` prop on `Tooltip`.
-- If the trigger is icon-only, the trigger must have its own accessible label independent of the tooltip text.
-
-**Context-dependent**
-- Focus moves into the modal when it opens.
-- Focus remains trapped inside the modal while it is open.
-- Focus returns to the invoking control when the modal closes.
-- Modal dialogs close with `Escape` and outside click unless disabled.
-- Menus move focus into the menu when they open and support keyboard navigation among items.
-- For state behavior, see **Interaction states**.
-
-**Forbidden**
-- Do not use `Select` for actions.
-- Do not put links, buttons, or forms inside a `Tooltip`.
+- **Tokens only** — never hardcode hex colors, pixel values, or font stacks. Always use `@contentful/f36-tokens`.
+- **Sentence case** — all labels, headings, buttons, menu items. Never Title Case or ALL CAPS.
+- **One primary button** per screen section maximum.
+- **Every input in a `FormControl`** — Label is required for accessibility.
+- **Every page has a `Header`** — Title variant for top-level, Breadcrumb for subpages.
+- **Color carries meaning** — never decorative. Always pair with text or icon.
+- **Phosphor icons only** — never Material, Heroicons, Feather, or Lucide.
+- **"Never mind"** — the cancel label for destructive confirmations. Not "Cancel".
+- **`Layout` not `Workbench`** — Workbench is deprecated.
+- **No custom shadows** — only `shadowDefault`, `shadowHeavy`, `shadowButton`, `insetBoxShadowDefault`.
+- **No border radius outside the scale** — 4px, 6px, 12px, 100px only.
---
-## Interaction & State rules
-
-This section defines behavior and interaction rules. Use it with **Component rules**, **Interaction states**, and **Feedback selection rules**. Do not restate component selection or structural rules here.
-
-### Sidebar
-
-**Required**
-- Sidebar navigation uses the global **Interaction states** model.
-- The active item is `Design System` by default.
-- The collapse control is an `IconButton` with `CaretLeft`.
-
-**Context-dependent**
-- Active nav items use `blue/100` (`#E8F5FF`) background fill.
-- Default nav items use transparent background.
-- Hovered nav items use `gray/100` background fill.
-- Active nav items use the duotone icon variant.
-
-### Content area
-
-**Required**
-- See **Base Layout**.
-
-### Buttons
-
-**Required**
-- Buttons use the global **Interaction states** model.
-- Loading uses `isLoading`.
-
-**Forbidden**
-- Do not disable a button and add a spinner manually.
+## Code conventions
-### Forms
+From the [Forma 36 code style guide](https://github.com/contentful/forma-36/blob/main/docs/code-style-guide.md):
-**Required**
-- Forms use the global **Interaction states** model.
-
-### Feedback & Status
-
-**Required**
-- See **Feedback selection rules**.
-
-### Navigation & Overlays
-
-**Required**
-- `Modal` behavior follows the global **Interaction states** model.
-- Menus support keyboard navigation among items.
-- Tooltips are non-critical and supplementary.
-
-**Context-dependent**
-- Focus moves into the menu when it opens.
-- Focus moves into the modal when it opens.
-- Focus remains trapped inside the modal while it is open.
-- Focus returns to the invoking control when the modal closes.
-- Modal closes with `Escape` and outside click unless disabled.
-
-### Keyboard and focus
-
-**Required**
-- All interactive elements are keyboard operable.
-- Tab order follows the rendered interaction order.
-
-### Semantic structure and state communication
-
-**Required**
-- Semantic DOM order matches visual order.
-
-### Header and layout accessibility
-
-**Required**
-- Breadcrumbs and back buttons have descriptive accessible labels.
-- Interactive elements placed in header actions or filters are keyboard accessible and clearly labeled.
-- Interactive content placed inside layout containers preserves keyboard navigation and focus management.
-
-**Context-dependent**
-- If multiple headers are present, use the `as` prop to avoid duplicate landmarks and preserve heading hierarchy.
+- Prefix boolean props with `is` (e.g. `isActive`, `isDisabled`, `isLoading`)
+- Prefix show/hide props with `with` (e.g. `withDragHandle`, `withCloseButton`)
+- Callback props use `on` prefix (`onClick`, `onChange`); handler functions use `handle` (`handleClick`)
+- Component type names use `ComponentNameProps` pattern
+- Use `camelCase` for all prop names
+- Pass string prop values with double quotes directly, not wrapped in `{}`
+- Prefer `className` over inline `style` unless manipulating dynamic values
---
-## Feedback selection rules
-
-### Selection logic
-
-**Required**
-- Use FormControl.ValidationMessage when the message describes an error on a specific input.
-- Use Note when the message must remain visible while the user reviews or acts on related content.
-- Use Notification when the message confirms the result of a user action and can dismiss after 6 seconds.
-- Use Modal when the user must confirm before proceeding or when the information is essential to the current workflow.
+## Dependencies
-### FormControl.ValidationMessage
+```bash
+npm install @contentful/f36-components @contentful/f36-tokens @contentful/f36-icons
+```
-**Use when**
+Requires React 18. Compatible with Vite. CSS import is mandatory:
-**Context-dependent**
-- the message is tied to a specific field
-- the field is invalid
+```tsx
+import '@contentful/f36-components/dist/styles.css';
+```
-**Do not use when**
-
-**Forbidden**
-- the message applies to the page, section, or workflow rather than a specific input
-- the message is only informational guidance
-
-### Note
-
-**Use when**
-
-**Context-dependent**
-- the message is proactive context the user needs before acting
-- the message must remain visible
-- the message belongs near the relevant content
-
-**Do not use when**
-
-**Forbidden**
-- the message is a field-specific validation error
-- the message is only a temporary action result
-- the message is placed at the top of the page generically
-
-### Notification
-
-**Use when**
-
-**Context-dependent**
-- the message is immediate feedback after a user action
-- the message can dismiss after 6 seconds
-- the message is an action result such as success or error
-
-**Do not use when**
-
-**Forbidden**
-- the message must remain visible while the user works
-- the message is persistent context
-- the message replaces inline validation for a specific field
-
-### Modal
-
-**Use when**
-
-**Context-dependent**
-- the user must confirm before proceeding
-- the information is essential to the current workflow
-- the interaction blocks the interface
-
-**Do not use when**
-
-**Forbidden**
-- the information is non-essential
-- the interaction interrupts high-stakes flows without confirmation need
-- the message is only a transient action result
-- the message is only persistent inline context
-
-### Forbidden patterns
-
-**Forbidden**
-- Do not use Notification for persistent context
-- Do not use Note for field validation
-- Do not use Modal for non-essential information
-- Do not use Modal for a transient success or error message
-- Do not use FormControl.ValidationMessage for page-level or section-level messaging
-
-### Acceptance criteria
-
-**Required**
-- A field-specific error uses FormControl.ValidationMessage
-- A message that must remain visible uses Note
-- A temporary action result uses Notification
-- A blocking confirmation uses Modal
-- Notification messages dismiss after 6 seconds
-- Note is placed near the relevant content
-- FormControl.ValidationMessage appears only when invalid
-- Modal is used only for confirmation before proceeding or information essential to the current workflow
+Font: Geist from `https://cdn.f36.contentful.com`. Apply `font-feature-settings: 'ss05' 1`.
---
-## Content & writing
+## File map
-### Writing rules
+Each folder contains an `overview.md` that indexes its contents.
-**Required**
-- Use active voice.
-- Use present tense.
-- Use sentence case everywhere.
-- Start button labels with a verb.
-- Start link labels with a verb when the link is an action.
-- Error messages must state what went wrong and what to do next.
-- Confirmation dialogs for destructive actions must state exactly what will be destroyed.
+### `guidelines/`
-**Forbidden**
-- Do not use "please".
-- Do not use "click here".
-- Do not use "learn more" without context.
-- Do not use vague single-word labels such as "Go" or "Submit".
-- Do not use Title Case.
-- Do not use ALL CAPS.
+| File | What it covers |
+|---|---|
+| `overview.md` | Design philosophy, product character, critical rules |
+| `setup.md` | Dependencies, CSS import, Geist font, token imports, icon library |
-**Context-dependent**
-- If a link is navigation rather than an action, the label must describe the destination clearly.
+### `guidelines/tokens/`
-### Button writing rules
+| File | What it covers |
+|---|---|
+| `overview.md` | Token philosophy and index |
+| `color.md` | Semantic colors, gray scale, blue/green/red/orange/purple/yellow palettes |
+| `typography.md` | Font families, sizes, weights, line heights, letter spacing, composite type roles |
+| `spacing.md` | 4px–80px spacing scale, content width tokens |
+| `effects.md` | Shadows (4 only), focus rings, border radius, z-index, transitions |
-**Required**
-- Button labels start with a verb.
-- Maximum button label length: 3 words.
-- The dismiss action for a destructive flow uses Never mind.
+### `guidelines/components/`
-**Forbidden**
-- Do not use Cancel as the dismiss action for a destructive flow.
+| File | What it covers |
+|---|---|
+| `overview.md` | Component decision tree and file index |
+| `layout.md` | `Box`, `Flex`, `Stack`, `Grid`, `Layout` |
+| `buttons.md` | `Button` (5 variants), `IconButton`, `TextLink`, `CopyButton` |
+| `forms.md` | `FormControl`, `TextInput`, `Textarea`, `Select`, `Autocomplete`, `Multiselect`, `Checkbox`, `Radio`, `Switch`, `DatePicker` |
+| `feedback.md` | `Notification`, `Note`, `Badge`, `EntityStatusBadge`, `Spinner` |
+| `navigation.md` | `Tabs`, `Modal`, `Menu`, `Tooltip`, `Popover`, `NavList` |
+| `data-display.md` | `Table`, `Card`, `EntityList`, `Accordion`, `Collapse`, `Skeleton`, `Pagination`, `List`, `ProgressStepper` |
+| `content-media.md` | `Asset`, `Avatar`, `Image`, `DateTime`, `UsageCard` |
+| `utility.md` | Typography components, `Header`, `DragHandle`, `Pill`, Icons |
+
+### `guidelines/composition/`
+
+| File | What it covers |
+|---|---|
+| `overview.md` | Composition philosophy and file index |
+| `base-shell.md` | Standard app chrome — top bar (52px), sidebar (240px), content area |
+| `screen-patterns.md` | 9 named patterns: list, detail, settings, empty state, error, confirmation, wizard, dashboard, form |
-### Modal writing rules
+### `guidelines/foundations/`
-**Required**
-- Modal title format: verb + noun.
-- Confirm button format: verb + noun.
-- Destructive confirmation copy states exactly what will be destroyed.
-- The dismiss action for a destructive flow uses Never mind.
+| File | What it covers |
+|---|---|
+| `overview.md` | Foundations index |
+| `writing.md` | Voice & tone, sentence case, button labels, error messages, forbidden phrases, status labels |
+| `icons.md` | Phosphor library, Regular vs Duotone, sizing (14/16/20px), color-by-role |
+| `accessibility.md` | WCAG contrast, keyboard navigation, ARIA labels, screen reader guidance, focus rings |
-**Forbidden**
-- Do not use a vague modal title.
-- Do not use a vague confirm label.
-- Do not use Cancel as the dismiss action for a destructive flow.
+### `examples/`
----
+Working TSX files demonstrating correct usage. Each maps to a screen pattern.
-## What Forma 36 Never Does
-
-**Forbidden**
-- Do not use decorative color.
-- Do not create custom shadows or glows.
-- Do not use rounded corners outside 4px, 6px, 12px, and 100px.
-- Do not use arbitrary spacing.
-- Do not use Title Case labels.
-- Do not use more than one primary button per screen section.
-- Do not use Button as a link. Use TextLink for navigation.
-- Do not use a bare Badge for entity status. Use EntityStatusBadge.
-- Do not use Notification for persistent context. Use Note and follow **Feedback selection rules**.
-- Do not place interactive elements inside Tooltip content.
-- Do not omit the Header from a page.
-- Do not use Workbench. Use Layout.
-- Do not use raw hex colors in code. Use tokens from @contentful/f36-tokens.
+| File | Pattern | What it shows |
+|---|---|---|
+| `list-page.tsx` | List / Index | Table, search, EntityStatusBadge, Menu actions, Pagination, empty state, Skeleton |
+| `detail-page.tsx` | Detail / Edit | Right sidebar, breadcrumbs, form validation, unsaved-changes Note |
+| `settings-page.tsx` | Settings | Grouped sections, Switch toggles, danger zone |
+| `confirmation-dialog.tsx` | Confirmation | Modal, "Never mind" cancel, `isLoading` confirm, `shouldCloseOnOverlayClick={false}` |
+| `form-page.tsx` | Standalone Form | `Layout variant="narrow"`, grouped FormControls, Accordion, Notification |
+| `error-state.tsx` | Error State | Centered error, WarningOctagonIcon, "Try again" + "Go back" |
diff --git a/skills/examples/confirmation-dialog.tsx b/skills/examples/confirmation-dialog.tsx
new file mode 100644
index 0000000000..24f1f3a5cf
--- /dev/null
+++ b/skills/examples/confirmation-dialog.tsx
@@ -0,0 +1,50 @@
+/**
+ * Confirmation Dialog — destructive action pattern.
+ * Title and confirm button always match. Cancel is "Never mind".
+ */
+import { Modal, Button, Paragraph } from '@contentful/f36-components';
+import { useState } from 'react';
+
+function DeleteContentTypeDialog() {
+ const [isOpen, setIsOpen] = useState(false);
+ const [isDeleting, setIsDeleting] = useState(false);
+
+ async function handleDelete() {
+ setIsDeleting(true);
+ // ... perform deletion
+ setIsDeleting(false);
+ setIsOpen(false);
+ }
+
+ return (
+ <>
+
+
+ setIsOpen(false)}
+ size="small"
+ shouldCloseOnOverlayClick={false}
+ >
+ setIsOpen(false)} />
+
+
+ This content type and all its fields will be permanently deleted. This cannot be undone.
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export { DeleteContentTypeDialog };
diff --git a/skills/examples/detail-page.tsx b/skills/examples/detail-page.tsx
new file mode 100644
index 0000000000..8e9e44923b
--- /dev/null
+++ b/skills/examples/detail-page.tsx
@@ -0,0 +1,84 @@
+/**
+ * Detail / Edit Page — single entity view with right sidebar.
+ */
+import {
+ Layout,
+ Header,
+ Button,
+ Stack,
+ FormControl,
+ TextInput,
+ Textarea,
+ EntityStatusBadge,
+ Note,
+ Paragraph,
+ SectionHeading,
+ Box,
+} from '@contentful/f36-components';
+
+function EntryDetailPage() {
+ return (
+
+
+
+
+
+ }
+ />
+
+ }
+ rightSidebar={
+
+
+ Status
+
+
+ Metadata
+ Created: Jan 15, 2025
+ Updated: 2 hours ago
+
+
+ }
+ >
+
+
+ You have unsaved changes that will be lost if you navigate away.
+
+
+
+
+ Title
+
+
+
+
+ Description
+
+
+ A short description displayed below the banner title.
+
+
+
+
+ Slug
+
+
+ This field is required. Add a value before publishing.
+
+
+
+
+
+ );
+}
+
+export { EntryDetailPage };
diff --git a/skills/examples/error-state.tsx b/skills/examples/error-state.tsx
new file mode 100644
index 0000000000..060a741f94
--- /dev/null
+++ b/skills/examples/error-state.tsx
@@ -0,0 +1,37 @@
+/**
+ * Error State — full-page error with recovery action.
+ */
+import { Flex, Heading, Paragraph, Button, Stack } from '@contentful/f36-components';
+import { WarningOctagonIcon } from '@contentful/f36-icons';
+import tokens from '@contentful/f36-tokens';
+
+function PageError({ section = 'content types' }: { section?: string }) {
+ return (
+
+
+ Couldn't load {section}
+
+ Something went wrong while loading this page. Check your connection and try again.
+
+
+
+
+
+
+ );
+}
+
+export { PageError };
diff --git a/skills/examples/form-page.tsx b/skills/examples/form-page.tsx
new file mode 100644
index 0000000000..0b9e1e6dcb
--- /dev/null
+++ b/skills/examples/form-page.tsx
@@ -0,0 +1,108 @@
+/**
+ * Standalone Form Page — narrow layout, grouped fields, inline validation.
+ */
+import {
+ Layout,
+ Header,
+ Button,
+ Stack,
+ FormControl,
+ TextInput,
+ Textarea,
+ Select,
+ Checkbox,
+ Heading,
+ Note,
+ Accordion,
+ Notification,
+} from '@contentful/f36-components';
+
+function CreateContentTypePage() {
+ function handleSave() {
+ Notification.success('Content type created');
+ }
+
+ return (
+
+
+
+
+
+ }
+ />
+
+ }
+ >
+
+
+ {/* Basic info */}
+
+ Basic information
+
+
+ Name
+
+
+ The display name for this content type.
+
+
+
+
+ API identifier
+
+
+ Used in API responses and SDK queries. Cannot be changed later.
+
+
+
+
+ Description
+
+
+
+
+ {/* Display settings */}
+
+ Display
+
+
+ Entry title field
+
+
+ The field used as the entry title in lists and references.
+
+
+
+
+ {/* Advanced — collapsed by default */}
+
+
+
+
+ These settings are optional and can be changed later.
+
+ Enable localization for all fields
+ Enable real-time collaboration
+
+
+
+
+
+
+ );
+}
+
+export { CreateContentTypePage };
diff --git a/skills/examples/list-page.tsx b/skills/examples/list-page.tsx
new file mode 100644
index 0000000000..ceccbcadc0
--- /dev/null
+++ b/skills/examples/list-page.tsx
@@ -0,0 +1,146 @@
+/**
+ * List / Index Page — the most common Contentful screen pattern.
+ * Shows a filterable, paginated table of content types.
+ */
+import {
+ Layout,
+ Header,
+ Button,
+ Table,
+ TextInput,
+ EntityStatusBadge,
+ Menu,
+ IconButton,
+ Pagination,
+ Stack,
+ Flex,
+ Paragraph,
+ Heading,
+ Skeleton,
+} from '@contentful/f36-components';
+import { PlusIcon, SearchIcon, MoreHorizontalIcon } from '@contentful/f36-icons';
+
+function ContentTypesListPage() {
+ return (
+
+ }>
+ Add content type
+
+ }
+ filters={
+ }
+ />
+ }
+ />
+
+ }
+ >
+
+
+
+
+ Name
+ Fields
+ Updated
+ Status
+
+
+
+
+
+ Blog Post
+ 12
+ 2 hours ago
+
+
+
+
+
+
+
+
+
+
+
+ {}}
+ totalItems={50}
+ itemsPerPage={20}
+ />
+
+
+
+ );
+}
+
+/**
+ * Empty state variant — shown when no content types exist yet.
+ */
+function ContentTypesEmptyState() {
+ return (
+
+ No content types yet
+
+ Content types define the structure of your content. Start by adding your first one.
+
+ }>
+ Add content type
+
+
+ );
+}
+
+/**
+ * Loading state — shown while data is being fetched.
+ */
+function ContentTypesLoading() {
+ return (
+
+
+
+ Name
+ Fields
+ Updated
+ Status
+
+
+
+
+
+
+
+ );
+}
+
+export { ContentTypesListPage, ContentTypesEmptyState, ContentTypesLoading };
diff --git a/skills/examples/settings-page.tsx b/skills/examples/settings-page.tsx
new file mode 100644
index 0000000000..0aa8a3c9ba
--- /dev/null
+++ b/skills/examples/settings-page.tsx
@@ -0,0 +1,84 @@
+/**
+ * Settings Page — feature configuration with danger zone.
+ */
+import {
+ Layout,
+ Header,
+ Button,
+ Stack,
+ FormControl,
+ TextInput,
+ Switch,
+ Select,
+ Heading,
+ Note,
+ Box,
+} from '@contentful/f36-components';
+import tokens from '@contentful/f36-tokens';
+
+function WebhookSettingsPage() {
+ return (
+
+ Save changes}
+ />
+
+ }
+ >
+
+
+ {/* General section */}
+
+ General
+
+
+ Webhook name
+
+
+
+
+ URL
+
+
+ The endpoint that receives the webhook payload.
+
+
+
+
+ Content type
+
+
+
+
+
+ {/* Triggers section */}
+
+ Triggers
+
+ Trigger on publish
+ Trigger on unpublish
+ Trigger on delete
+
+
+
+ {/* Danger zone */}
+
+
+ Deleting this webhook is permanent and cannot be undone.
+
+
+
+
+
+
+ );
+}
+
+export { WebhookSettingsPage };
diff --git a/skills/guidelines/components/buttons.md b/skills/guidelines/components/buttons.md
new file mode 100644
index 0000000000..16b2884c59
--- /dev/null
+++ b/skills/guidelines/components/buttons.md
@@ -0,0 +1,87 @@
+# Buttons
+
+## Button
+
+Four variants with clear hierarchy:
+
+| Variant | Color | Use | Limit |
+|---|---|---|---|
+| `primary` | Blue | The single most important action | **One per screen section** |
+| `secondary` | Gray outline | Supporting actions | Default choice |
+| `positive` | Green | Constructive: create, publish, add | |
+| `negative` | Red | Destructive: delete, remove, archive | |
+| `transparent` | None | Lowest priority; light backgrounds only | |
+
+**Sizes:** `small` (toolbars), `medium` (default), `large` (rare, hero CTAs only)
+
+```tsx
+}>Add field
+
+
+
+```
+
+| Prop | Type | Notes |
+|---|---|---|
+| `variant` | `'primary'` `'secondary'` `'positive'` `'negative'` `'transparent'` | |
+| `size` | `'small'` `'medium'` `'large'` | |
+| `startIcon` / `endIcon` | ReactNode | Icon element |
+| `isDisabled` | boolean | |
+| `isLoading` | boolean | Shows spinner — never disable + add spinner manually |
+| `isFullWidth` | boolean | |
+| `as` | ElementType | Use `as="a"` with `href` for navigation buttons |
+
+**Writing rules:**
+- Always start with a verb: "Add field", "Delete entry", "Publish"
+- Max 3 words
+- Sentence case
+- Cancel for destructive actions: **"Never mind"** — not "Cancel"
+
+## IconButton
+
+Icon-only button with built-in tooltip from `aria-label`.
+
+```tsx
+} variant="secondary" aria-label="More actions" />
+```
+
+| Prop | Type | Notes |
+|---|---|---|
+| `icon` | ReactNode | Required |
+| `variant` | Same as Button | |
+| `aria-label` | string | Required — doubles as tooltip |
+| `size` | Same as Button | |
+
+Do not use IconButton for navigation.
+
+## TextLink
+
+For navigation and low-priority actions. Never use Button for navigation.
+
+```tsx
+Settings
+Clear filters
+```
+
+| Prop | Type | Notes |
+|---|---|---|
+| `as` | `'a'` (default) or `'button'` | `a` = navigation, `button` = action |
+| `variant` | `'primary'` `'positive'` `'negative'` `'secondary'` `'muted'` `'white'` `'premium'` | |
+| `icon` | ReactNode | |
+| `alignIcon` | `'start'` `'end'` | |
+
+Use `rel="noreferrer noopener"` with `target="_blank"`.
+
+## CopyButton
+
+Place at the end of content the user wants to copy.
+
+```tsx
+
+```
+
+| Prop | Type | Notes |
+|---|---|---|
+| `value` | string | Required — the text to copy |
+| `tooltipText` | string | Default: "Copy to clipboard" |
+| `tooltipCopiedText` | string | Default: "Copied!" |
diff --git a/skills/guidelines/components/content-media.md b/skills/guidelines/components/content-media.md
new file mode 100644
index 0000000000..18922f902e
--- /dev/null
+++ b/skills/guidelines/components/content-media.md
@@ -0,0 +1,65 @@
+# Content & Media
+
+## Asset
+
+Represents an asset visually without displaying the file itself.
+
+```tsx
+
+```
+
+Types: `image`, `pdf`, `video`, `audio`, `code`, `spreadsheet`. When `type="image"` renders ``. Archived assets show an icon.
+
+## Avatar
+
+```tsx
+
+
+```
+
+| Prop | Values |
+|---|---|
+| `size` | `'tiny'` (20px) `'small'` (24px) `'medium'` (32px) `'large'` (48px) |
+| `variant` | `'user'` `'app'` |
+| `colorVariant` | `'gray'` `'green'` `'purple'` `'pink'` `'muted'` |
+| `src` | Image URL |
+| `initials` | Fallback text |
+
+Pass meaningful `alt` when used without adjacent name text.
+
+## Image
+
+Basic image component.
+
+```tsx
+
+```
+
+## DateTime
+
+Display-only date/time. Renders a `