From 21877ce4e6fde7d23c121536cc94d2df5025a558 Mon Sep 17 00:00:00 2001 From: Richie Date: Wed, 25 Mar 2026 16:45:24 +1100 Subject: [PATCH] Add Chip atom component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Interactive tag for filtering, selection, and dismissible labels - Wraps MUI Chip with FA tokens: 12 component tokens (height, padding, font, icon sizes) - Two variants (filled/outlined) × two colours (default/primary) × two sizes (sm/md) - Custom `selected` prop promotes to brand colour with warm bg (outlined) - MUI theme overrides: soft tonal fills, branded outlines, hover/focus states - 10 Storybook stories including interactive filter and removable tag demos - Preflight passed all 5 checks Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/memory/component-registry.md | 2 +- docs/memory/session-log.md | 47 ++- docs/memory/token-registry.md | 19 ++ src/components/atoms/Chip/Chip.stories.tsx | 361 +++++++++++++++++++++ src/components/atoms/Chip/Chip.tsx | 76 +++++ src/components/atoms/Chip/index.ts | 2 + src/theme/generated/tokens.css | 12 + src/theme/generated/tokens.js | 12 + src/theme/index.ts | 57 ++++ tokens/component/chip.json | 45 +++ 10 files changed, 631 insertions(+), 2 deletions(-) create mode 100644 src/components/atoms/Chip/Chip.stories.tsx create mode 100644 src/components/atoms/Chip/Chip.tsx create mode 100644 src/components/atoms/Chip/index.ts create mode 100644 tokens/component/chip.json diff --git a/docs/memory/component-registry.md b/docs/memory/component-registry.md index 4a4e4bb..e60749c 100644 --- a/docs/memory/component-registry.md +++ b/docs/memory/component-registry.md @@ -24,7 +24,7 @@ duplicates) and MUST update it after completing one. | Icon | planned | various sizes | | Icon wrapper component | | Avatar | planned | image, initials, icon × small, medium, large | | User/entity representation | | Divider | planned | horizontal, vertical | | Visual separator | -| Chip | planned | filled, outlined × deletable, clickable | | Tags and filters | +| Chip | review | filled, outlined × small, medium × clickable, deletable, selected × default, primary | chip.height/paddingX/fontSize/iconSize/deleteIconSize/iconGap/borderRadius, color.neutral.200-700, color.brand.200-700 | Interactive tag. Wraps MUI Chip with FA tokens. Selected state promotes to brand colour. Filled uses soft tonal bg (like Badge). | | Card | done | elevated, outlined × default, compact, none padding × interactive × selected | card.borderRadius/padding/shadow/border/background, color.surface.raised/subtle/warm, color.border.default/brand, shadow.md/lg | Content container. Elevated (shadow) or outlined (border). Interactive adds hover bg fill + shadow lift. Selected adds brand border + warm bg. Three padding presets. | | Link | planned | default, subtle | | Navigation text | diff --git a/docs/memory/session-log.md b/docs/memory/session-log.md index 1b9a365..85c0a14 100644 --- a/docs/memory/session-log.md +++ b/docs/memory/session-log.md @@ -411,6 +411,51 @@ Each entry follows this structure: **Next steps:** - Review Badge in Storybook (large size was added per user request) -- Build Chip atom (interactive tags — clickable, deletable, with icon support) +- ~~Build Chip atom (interactive tags — clickable, deletable, with icon support)~~ ✓ Done - Consider running /audit or /critique on completed atoms to establish baseline scores - Begin PriceCard molecule once Chip is done (depends on Card + Badge + Typography + Button) + +### Session 2026-03-25j — Chip atom + +**Agent(s):** Claude Opus (via conversation) + +**Work completed:** +- Created chip component tokens (`tokens/component/chip.json`): height, paddingX, fontSize, iconSize, deleteIconSize, iconGap, borderRadius for sm/md — 12 tokens total +- Rebuilt token pipeline — 12 new chip tokens generated across CSS/JS/JSON outputs +- Updated MUI theme (`src/theme/index.ts`) with MuiChip overrides: + - Pill-shaped borderRadius from token + - Two sizes: small (28px), medium (32px) + - Filled variant: neutral.200 bg for default, brand.200 bg for primary (soft/tonal, matching Badge and Button soft) + - Outlined variant: neutral.300 border for default, brand.400 border for primary + - Hover states per variant × colour + - Delete icon hover colour change + - Focus-visible outline (brand.500, 2px offset) +- Created Chip component (`src/components/atoms/Chip/Chip.tsx`): + - Wraps MUI Chip with React.forwardRef + - Custom `selected` prop: promotes colour to primary, outlined selected gets brand border + warm bg + - Two variants: filled (tonal, default), outlined (border) + - Two colours: default (neutral), primary (brand) + - Interactive modes: clickable (via onClick), deletable (via onDelete), both, or static + - All MUI ChipProps passed through +- Created barrel export (`src/components/atoms/Chip/index.ts`) +- Created 10 Storybook stories: Default, Variants, Sizes, WithIcons, Clickable, Deletable, Selected, FilterChips (interactive toggle demo), RemovableTags (interactive dismiss demo), InServiceOption (realistic card composition), CompleteMatrix +- Preflight passed all 5 checks + +**Decisions made:** +- Chip uses MUI Chip wrapper (unlike Badge which uses raw Box) — MUI Chip provides built-in keyboard navigation, delete icon, and click handling that would be complex to reimplement +- Chip defaults to `filled` variant with soft/tonal colours (matching Badge soft and Button soft patterns) — NOT MUI's default opaque fill +- Only `default` and `primary` colours initially — secondary/error/etc. added when needed (YAGNI) +- Selected state auto-promotes to primary colour unless colour is explicitly set — mirrors Card's selected brand treatment +- No new design decision IDs needed — Chip follows established conventions + +**Component status at end of session:** +- **Done (5):** Button, Typography, Input, Card, Badge +- **Review (1):** Chip +- **Planned (4 atoms):** IconButton, Icon, Avatar, Divider, Link +- **Planned (5 molecules):** FormField, PriceCard, ServiceOption, SearchBar, StepIndicator +- **Planned (5 organisms):** ServiceSelector, PricingTable, ArrangementForm, Navigation, Footer + +**Next steps:** +- User to review Chip in Storybook +- Begin PriceCard molecule (depends on Card + Badge + Typography + Button + Chip) +- Consider running /audit or /critique on completed atoms to establish baseline scores diff --git a/docs/memory/token-registry.md b/docs/memory/token-registry.md index 32f8ba1..f1facb1 100644 --- a/docs/memory/token-registry.md +++ b/docs/memory/token-registry.md @@ -225,3 +225,22 @@ the correct token for any design property. | badge.iconSize.md | 14px | Badge | Medium badge icon | | badge.iconGap.default | → spacing.1 (4px) | Badge | Icon-to-text gap | | badge.borderRadius.default | → borderRadius.full (9999px) | Badge | Pill shape | + +### Chip + +`tokens/component/chip.json` + +| Token path | Value / Reference | Used by | Description | +|-----------|-----------|---------|-------------| +| chip.height.sm | 28px | Chip | Small chip height | +| chip.height.md | 32px | Chip | Medium chip height (default) | +| chip.paddingX.sm | → spacing.2 (8px) | Chip | Small horizontal padding | +| chip.paddingX.md | → spacing.3 (12px) | Chip | Medium horizontal padding | +| chip.fontSize.sm | → fontSize.xs (12px) | Chip | Small chip text | +| chip.fontSize.md | → fontSize.sm (14px) | Chip | Medium chip text | +| chip.iconSize.sm | 16px | Chip | Small chip leading icon | +| chip.iconSize.md | 18px | Chip | Medium chip leading icon | +| chip.deleteIconSize.sm | 14px | Chip | Small chip delete icon | +| chip.deleteIconSize.md | 16px | Chip | Medium chip delete icon | +| chip.iconGap.default | → spacing.1 (4px) | Chip | Icon-to-text gap | +| chip.borderRadius.default | → borderRadius.full (9999px) | Chip | Pill shape | diff --git a/src/components/atoms/Chip/Chip.stories.tsx b/src/components/atoms/Chip/Chip.stories.tsx new file mode 100644 index 0000000..3f83b17 --- /dev/null +++ b/src/components/atoms/Chip/Chip.stories.tsx @@ -0,0 +1,361 @@ +import React, { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { Chip } from './Chip'; +import { Card } from '../Card'; +import { Typography } from '../Typography'; +import Box from '@mui/material/Box'; +import LocalOfferIcon from '@mui/icons-material/LocalOffer'; +import FaceIcon from '@mui/icons-material/Face'; +import CheckIcon from '@mui/icons-material/Check'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import ChurchIcon from '@mui/icons-material/Church'; +import LocalFloristIcon from '@mui/icons-material/LocalFlorist'; +import DirectionsCarIcon from '@mui/icons-material/DirectionsCar'; +import RestaurantIcon from '@mui/icons-material/Restaurant'; +import MusicNoteIcon from '@mui/icons-material/MusicNote'; +import PhotoCameraIcon from '@mui/icons-material/PhotoCamera'; + +const meta: Meta = { + title: 'Atoms/Chip', + component: Chip, + tags: ['autodocs'], + parameters: { + layout: 'centered', + }, + argTypes: { + variant: { + control: 'select', + options: ['filled', 'outlined'], + description: 'Visual style variant', + table: { defaultValue: { summary: 'filled' } }, + }, + color: { + control: 'select', + options: ['default', 'primary'], + description: 'Colour intent', + table: { defaultValue: { summary: 'default' } }, + }, + size: { + control: 'select', + options: ['small', 'medium'], + description: 'Size preset', + table: { defaultValue: { summary: 'medium' } }, + }, + selected: { + control: 'boolean', + description: 'Selected/active state', + table: { defaultValue: { summary: 'false' } }, + }, + clickable: { + control: 'boolean', + description: 'Whether the chip is clickable', + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// ─── Default ──────────────────────────────────────────────────────────────── + +/** Default chip — filled variant, neutral colour */ +export const Default: Story = { + args: { + label: 'Chip label', + }, +}; + +// ─── Variants ─────────────────────────────────────────────────────────────── + +/** Both visual variants with default and primary colour */ +export const Variants: Story = { + render: () => ( + + + + + + + ), +}; + +// ─── Sizes ────────────────────────────────────────────────────────────────── + +/** Small and medium sizes side by side */ +export const Sizes: Story = { + render: () => ( + + } /> + } /> + } /> + } /> + + ), +}; + +// ─── With Icons ───────────────────────────────────────────────────────────── + +/** Chips with leading icons */ +export const WithIcons: Story = { + name: 'With Icons', + render: () => ( + + } label="Chapel" /> + } label="Flowers" color="primary" /> + } label="Transport" variant="outlined" /> + } label="Family" variant="outlined" color="primary" /> + + ), +}; + +// ─── Clickable ────────────────────────────────────────────────────────────── + +/** Clickable chips respond to click events (for filtering/toggling) */ +export const Clickable: Story = { + render: () => ( + + {}} /> + {}} /> + {}} /> + {}} /> + + ), +}; + +// ─── Deletable ────────────────────────────────────────────────────────────── + +/** Deletable chips show a close icon */ +export const Deletable: Story = { + render: () => ( + + {}} /> + {}} /> + {}} /> + } onDelete={() => {}} /> + + ), +}; + +// ─── Selected State ───────────────────────────────────────────────────────── + +/** Selected state promotes chip to brand colour */ +export const Selected: Story = { + render: () => ( + + + Filled + + {}} /> + {}} /> + + + + Outlined + + {}} /> + {}} /> + + + + ), +}; + +// ─── Interactive: Filter Chips ────────────────────────────────────────────── + +/** + * Toggle filter pattern — commonly used for service category filtering. + * Click chips to toggle selection. + */ +export const FilterChips: Story = { + name: 'Interactive — Filter Chips', + render: () => { + const categories = [ + { label: 'Chapel', icon: }, + { label: 'Flowers', icon: }, + { label: 'Transport', icon: }, + { label: 'Catering', icon: }, + { label: 'Music', icon: }, + { label: 'Photography', icon: }, + ]; + + const FilterDemo = () => { + const [selected, setSelected] = useState>(new Set(['Chapel', 'Flowers'])); + + const toggle = (label: string) => { + setSelected((prev) => { + const next = new Set(prev); + if (next.has(label)) next.delete(label); + else next.add(label); + return next; + }); + }; + + return ( + + + + Filter services + + + {categories.map(({ label, icon }) => ( + : icon} + selected={selected.has(label)} + onClick={() => toggle(label)} + variant="outlined" + /> + ))} + + + Selected: {selected.size === 0 ? 'None' : Array.from(selected).join(', ')} + + + ); + }; + + return ; + }, +}; + +// ─── Interactive: Removable Tags ──────────────────────────────────────────── + +/** + * Removable tag pattern — for selected items that can be dismissed. + * Click the × icon to remove a tag. + */ +export const RemovableTags: Story = { + name: 'Interactive — Removable Tags', + render: () => { + const TagDemo = () => { + const [tags, setTags] = useState([ + 'White roses', + 'Organ music', + 'Prayer cards', + 'Memorial video', + 'Guest book', + ]); + + const remove = (tag: string) => { + setTags((prev) => prev.filter((t) => t !== tag)); + }; + + return ( + + + Selected additions ({tags.length}) + + + {tags.length === 0 ? ( + + No items selected + + ) : ( + tags.map((tag) => ( + remove(tag)} + /> + )) + )} + + + ); + }; + + return ; + }, +}; + +// ─── In Context: Service Option ───────────────────────────────────────────── + +/** + * Chips used inside a ServiceOption-style card layout, + * showing service tags and category labels. + */ +export const InServiceOption: Story = { + name: 'In Context — Service Option', + render: () => ( + + + + Chapel Ceremony + $1,200 + + + Traditional chapel service with celebrant and music of your choosing. + + + } label="Indoor" /> + } label="Music included" /> + + + + + + + Graveside Service + $900 + + + Intimate outdoor farewell at the burial site. + + + + + + + + + ), +}; + +// ─── Complete Matrix ──────────────────────────────────────────────────────── + +/** Full variant × colour × size × state matrix for visual QA */ +export const CompleteMatrix: Story = { + name: 'Complete Matrix', + render: () => { + const variants = ['filled', 'outlined'] as const; + const colors = ['default', 'primary'] as const; + const sizes = ['medium', 'small'] as const; + + return ( + + {variants.map((variant) => ( + + + {variant} + + + {sizes.map((size) => ( + + {size} + {colors.map((color) => ( + + + } /> + {}} /> + + ))} + + ))} + + + ))} + + + Selected state + + {}} /> + {}} /> + } onClick={() => {}} /> + + + + ); + }, +}; diff --git a/src/components/atoms/Chip/Chip.tsx b/src/components/atoms/Chip/Chip.tsx new file mode 100644 index 0000000..12b8aec --- /dev/null +++ b/src/components/atoms/Chip/Chip.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import MuiChip from '@mui/material/Chip'; +import type { ChipProps as MuiChipProps } from '@mui/material/Chip'; + +// ─── Types ─────────────────────────────────────────────────────────────────── + +/** Props for the FA Chip component */ +export interface ChipProps extends MuiChipProps { + /** Whether the chip is in a selected/active state */ + selected?: boolean; +} + +// ─── Component ─────────────────────────────────────────────────────────────── + +/** + * Interactive tag for the FA design system. + * + * Pill-shaped chip for filtering, selection, and dismissible labels. + * Used in ServiceOption (service tags), search filters, arrangement forms, + * and anywhere users interact with categorised content. + * + * Unlike Badge (display-only), Chip is interactive — it can be clicked + * (for toggling/selection) or deleted (for dismissing/removing). + * + * Variant mapping: + * - `filled` (default) — soft tonal background, like Button's soft variant + * - `outlined` — border-only, lighter visual weight + * + * Colour options: + * - `default` — neutral grey (general tags, filters) + * - `primary` — warm brand (selected states, category tags) + * + * Interactive modes: + * - **Clickable** — pass `onClick` or set `clickable` for toggle/filter chips + * - **Deletable** — pass `onDelete` for dismissible tags + * - **Both** — clickable + deletable for full interactive chips + * - **Static** — no onClick/onDelete for display-only tags (prefer Badge for pure status) + * + * Selected state: + * - `selected` prop applies brand styling (filled primary bg or outlined primary border) + */ +export const Chip = React.forwardRef( + ( + { + selected = false, + variant = 'filled', + color, + sx, + ...props + }, + ref, + ) => { + // When selected, promote to primary colour unless explicitly set + const resolvedColor = color ?? (selected ? 'primary' : 'default'); + + return ( + + ); + }, +); + +Chip.displayName = 'Chip'; +export default Chip; diff --git a/src/components/atoms/Chip/index.ts b/src/components/atoms/Chip/index.ts new file mode 100644 index 0000000..a8cd4b8 --- /dev/null +++ b/src/components/atoms/Chip/index.ts @@ -0,0 +1,2 @@ +export { Chip, default } from './Chip'; +export type { ChipProps } from './Chip'; diff --git a/src/theme/generated/tokens.css b/src/theme/generated/tokens.css index dccfc04..f727a6e 100644 --- a/src/theme/generated/tokens.css +++ b/src/theme/generated/tokens.css @@ -17,6 +17,12 @@ --fa-button-icon-size-sm: 16px; /** 16px icons in small buttons */ --fa-button-icon-size-md: 18px; /** 18px icons in medium buttons */ --fa-button-icon-size-lg: 20px; /** 20px icons in large buttons */ + --fa-chip-height-sm: 28px; /** Small — compact inline tags, tight layouts */ + --fa-chip-height-md: 32px; /** Medium — default interactive chips, filter tags */ + --fa-chip-icon-size-sm: 16px; /** 16px icons in small chips */ + --fa-chip-icon-size-md: 18px; /** 18px icons in medium chips */ + --fa-chip-delete-icon-size-sm: 14px; /** 14px delete icon in small chips */ + --fa-chip-delete-icon-size-md: 16px; /** 16px delete icon in medium chips */ --fa-input-height-sm: 40px; /** Small — compact forms, admin layouts, matches Button medium height */ --fa-input-height-md: 48px; /** Medium (default) — standard forms, matches Button large for alignment */ --fa-input-icon-size-default: 20px; /** 20px — icon size inside input field, matches Figma trailing icon */ @@ -242,6 +248,12 @@ --fa-card-padding-compact: var(--fa-spacing-4); /** 16px — compact card padding (mobile, tight layouts) */ --fa-card-shadow-default: var(--fa-shadow-md); /** Medium shadow — resting elevated card */ --fa-card-shadow-hover: var(--fa-shadow-lg); /** High shadow — interactive card on hover */ + --fa-chip-padding-x-sm: var(--fa-spacing-2); /** 8px — compact horizontal padding */ + --fa-chip-padding-x-md: var(--fa-spacing-3); /** 12px — default horizontal padding */ + --fa-chip-font-size-sm: var(--fa-font-size-xs); /** 12px — small chip text */ + --fa-chip-font-size-md: var(--fa-font-size-sm); /** 14px — default chip text */ + --fa-chip-icon-gap-default: var(--fa-spacing-1); /** 4px icon-text gap */ + --fa-chip-border-radius-default: var(--fa-border-radius-full); /** Pill shape — fully rounded */ --fa-input-padding-x-default: var(--fa-spacing-3); /** 12px — inner horizontal padding matching Figma design */ --fa-input-padding-y-sm: var(--fa-spacing-2); /** 8px — compact vertical padding for small size */ --fa-input-padding-y-md: var(--fa-spacing-3); /** 12px — standard vertical padding for medium size */ diff --git a/src/theme/generated/tokens.js b/src/theme/generated/tokens.js index 0d6fd49..fa94681 100644 --- a/src/theme/generated/tokens.js +++ b/src/theme/generated/tokens.js @@ -51,6 +51,18 @@ export const CardBorderSelected = "#ba834e"; // Brand border for selected/active export const CardBackgroundDefault = "#ffffff"; // White — standard card background (raised surface) export const CardBackgroundHover = "#fafafa"; // Subtle grey fill on hover — neutral.50 for soft interactive feedback export const CardBackgroundSelected = "#fef9f5"; // Warm tint for selected cards — brand.50 reinforces active state +export const ChipHeightSm = "28px"; // Small — compact inline tags, tight layouts +export const ChipHeightMd = "32px"; // Medium — default interactive chips, filter tags +export const ChipPaddingXSm = "8px"; // 8px — compact horizontal padding +export const ChipPaddingXMd = "12px"; // 12px — default horizontal padding +export const ChipFontSizeSm = "0.75rem"; // 12px — small chip text +export const ChipFontSizeMd = "0.875rem"; // 14px — default chip text +export const ChipIconSizeSm = "16px"; // 16px icons in small chips +export const ChipIconSizeMd = "18px"; // 18px icons in medium chips +export const ChipDeleteIconSizeSm = "14px"; // 14px delete icon in small chips +export const ChipDeleteIconSizeMd = "16px"; // 16px delete icon in medium chips +export const ChipIconGapDefault = "4px"; // 4px icon-text gap +export const ChipBorderRadiusDefault = "9999px"; // Pill shape — fully rounded export const InputHeightSm = "40px"; // Small — compact forms, admin layouts, matches Button medium height export const InputHeightMd = "48px"; // Medium (default) — standard forms, matches Button large for alignment export const InputPaddingXDefault = "12px"; // 12px — inner horizontal padding matching Figma design diff --git a/src/theme/index.ts b/src/theme/index.ts index 00b2489..49796aa 100644 --- a/src/theme/index.ts +++ b/src/theme/index.ts @@ -577,6 +577,63 @@ export const theme = createTheme({ }, }, }, + MuiChip: { + defaultProps: { + size: 'medium', + }, + styleOverrides: { + root: { + borderRadius: parseInt(t.ChipBorderRadiusDefault, 10), + fontWeight: 500, + letterSpacing: '0.01em', + transition: 'background-color 150ms ease-in-out, border-color 150ms ease-in-out, box-shadow 150ms ease-in-out', + '&:focus-visible': { + outline: `2px solid ${t.ColorInteractiveFocus}`, + outlineOffset: '2px', + }, + }, + sizeMedium: { + height: parseInt(t.ChipHeightMd, 10), + fontSize: t.ChipFontSizeMd, + '& .MuiChip-icon': { fontSize: t.ChipIconSizeMd, marginLeft: t.ChipPaddingXMd }, + '& .MuiChip-deleteIcon': { fontSize: t.ChipDeleteIconSizeMd, marginRight: t.ChipPaddingXMd }, + }, + sizeSmall: { + height: parseInt(t.ChipHeightSm, 10), + fontSize: t.ChipFontSizeSm, + '& .MuiChip-icon': { fontSize: t.ChipIconSizeSm, marginLeft: t.ChipPaddingXSm }, + '& .MuiChip-deleteIcon': { fontSize: t.ChipDeleteIconSizeSm, marginRight: t.ChipPaddingXSm }, + }, + filled: { + '&.MuiChip-colorDefault': { + backgroundColor: t.ColorNeutral200, + color: t.ColorNeutral700, + '&:hover': { backgroundColor: t.ColorNeutral300 }, + '& .MuiChip-deleteIcon': { color: t.ColorNeutral500, '&:hover': { color: t.ColorNeutral700 } }, + }, + '&.MuiChip-colorPrimary': { + backgroundColor: t.ColorBrand200, + color: t.ColorBrand700, + '&:hover': { backgroundColor: t.ColorBrand300 }, + '& .MuiChip-deleteIcon': { color: t.ColorBrand400, '&:hover': { color: t.ColorBrand700 } }, + }, + }, + outlined: { + '&.MuiChip-colorDefault': { + borderColor: t.ColorNeutral300, + color: t.ColorNeutral700, + '&:hover': { backgroundColor: t.ColorNeutral100, borderColor: t.ColorNeutral400 }, + '& .MuiChip-deleteIcon': { color: t.ColorNeutral400, '&:hover': { color: t.ColorNeutral700 } }, + }, + '&.MuiChip-colorPrimary': { + borderColor: t.ColorBrand400, + color: t.ColorBrand700, + '&:hover': { backgroundColor: t.ColorBrand100, borderColor: t.ColorBrand500 }, + '& .MuiChip-deleteIcon': { color: t.ColorBrand400, '&:hover': { color: t.ColorBrand700 } }, + }, + }, + }, + }, }, }); diff --git a/tokens/component/chip.json b/tokens/component/chip.json new file mode 100644 index 0000000..febadf2 --- /dev/null +++ b/tokens/component/chip.json @@ -0,0 +1,45 @@ +{ + "chip": { + "$description": "Chip component tokens — interactive tags for filtering, selection, and dismissible labels. Used in ServiceOption, search filters, and arrangement forms.", + "height": { + "$type": "dimension", + "$description": "Chip heights per size.", + "sm": { "$value": "28px", "$description": "Small — compact inline tags, tight layouts" }, + "md": { "$value": "32px", "$description": "Medium — default interactive chips, filter tags" } + }, + "paddingX": { + "$type": "dimension", + "$description": "Horizontal padding per size.", + "sm": { "$value": "{spacing.2}", "$description": "8px — compact horizontal padding" }, + "md": { "$value": "{spacing.3}", "$description": "12px — default horizontal padding" } + }, + "fontSize": { + "$type": "dimension", + "$description": "Font size per chip size.", + "sm": { "$value": "{fontSize.xs}", "$description": "12px — small chip text" }, + "md": { "$value": "{fontSize.sm}", "$description": "14px — default chip text" } + }, + "iconSize": { + "$type": "dimension", + "$description": "Leading icon dimensions per chip size.", + "sm": { "$value": "16px", "$description": "16px icons in small chips" }, + "md": { "$value": "18px", "$description": "18px icons in medium chips" } + }, + "deleteIconSize": { + "$type": "dimension", + "$description": "Delete/close icon dimensions per chip size.", + "sm": { "$value": "14px", "$description": "14px delete icon in small chips" }, + "md": { "$value": "16px", "$description": "16px delete icon in medium chips" } + }, + "iconGap": { + "$type": "dimension", + "$description": "Gap between icon/delete-icon and label text.", + "default": { "$value": "{spacing.1}", "$description": "4px icon-text gap" } + }, + "borderRadius": { + "$type": "dimension", + "$description": "Chip corner radius.", + "default": { "$value": "{borderRadius.full}", "$description": "Pill shape — fully rounded" } + } + } +}