Add selected state and hover background to Card
- Add `selected` prop: brand border (warm gold) + warm background tint (brand.50) - Add hover background fill (neutral.50) for interactive cards - Add 3 new card tokens: border.selected, background.hover, background.selected - Add stories: Selected, OptionSelect (single-select), MultiSelect (toggle), OnDifferentBackgrounds (white vs grey surface comparison) - Informed by FA 1.0 Figma ListItemPurchaseOption pattern (node 2349:39505) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
import { Card } from './Card';
|
import { Card } from './Card';
|
||||||
import { Typography } from '../Typography';
|
import { Typography } from '../Typography';
|
||||||
@@ -20,7 +21,12 @@ const meta: Meta<typeof Card> = {
|
|||||||
},
|
},
|
||||||
interactive: {
|
interactive: {
|
||||||
control: 'boolean',
|
control: 'boolean',
|
||||||
description: 'Adds hover shadow lift and pointer cursor',
|
description: 'Adds hover background fill, shadow lift, and pointer cursor',
|
||||||
|
table: { defaultValue: { summary: 'false' } },
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Highlights the card as selected — brand border + warm background',
|
||||||
table: { defaultValue: { summary: 'false' } },
|
table: { defaultValue: { summary: 'false' } },
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
@@ -89,7 +95,7 @@ export const Variants: Story = {
|
|||||||
|
|
||||||
// ─── Interactive ────────────────────────────────────────────────────────────
|
// ─── Interactive ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Interactive cards with hover elevation (click/hover to see effect) */
|
/** Interactive cards with hover background fill and shadow lift */
|
||||||
export const Interactive: Story = {
|
export const Interactive: Story = {
|
||||||
render: () => (
|
render: () => (
|
||||||
<div style={{ display: 'flex', gap: 24, maxWidth: 800 }}>
|
<div style={{ display: 'flex', gap: 24, maxWidth: 800 }}>
|
||||||
@@ -103,7 +109,7 @@ export const Interactive: Story = {
|
|||||||
Elevated + Interactive
|
Elevated + Interactive
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
Hover to see the shadow lift. Click to trigger action.
|
Hover to see the background fill and shadow lift.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
@@ -117,13 +123,223 @@ export const Interactive: Story = {
|
|||||||
Outlined + Interactive
|
Outlined + Interactive
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
Outlined cards can also be interactive with hover effects.
|
Outlined cards get a subtle background fill on hover.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ─── Selected ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Selected state — brand border + warm background tint */
|
||||||
|
export const Selected: Story = {
|
||||||
|
render: () => (
|
||||||
|
<div style={{ display: 'flex', gap: 24, maxWidth: 800 }}>
|
||||||
|
<Card variant="outlined" sx={{ flex: 1 }}>
|
||||||
|
<Typography variant="h5" gutterBottom>
|
||||||
|
Not selected
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Standard outlined card in its resting state.
|
||||||
|
</Typography>
|
||||||
|
</Card>
|
||||||
|
<Card variant="outlined" selected sx={{ flex: 1 }}>
|
||||||
|
<Typography variant="h5" gutterBottom>
|
||||||
|
Selected
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Brand border and warm background tint show this is the active choice.
|
||||||
|
</Typography>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── Option Select Pattern ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interactive option selection matching the FA 1.0 "ListItemPurchaseOption" pattern.
|
||||||
|
* Click a card to select it. Matches the Figma states:
|
||||||
|
* inactive → hover (bg fill) → active (brand border + warm bg).
|
||||||
|
*/
|
||||||
|
export const OptionSelect: Story = {
|
||||||
|
name: 'Option Select',
|
||||||
|
render: function OptionSelectDemo() {
|
||||||
|
const [selectedId, setSelectedId] = useState<string | null>('chapel');
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
id: 'chapel',
|
||||||
|
title: 'Chapel service',
|
||||||
|
desc: 'Traditional ceremony in our heritage-listed chapel, seating up to 120 guests.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'graveside',
|
||||||
|
title: 'Graveside service',
|
||||||
|
desc: 'An intimate outdoor farewell at the final resting place.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'memorial',
|
||||||
|
title: 'Memorial service',
|
||||||
|
desc: 'A celebration of life gathering at a venue of your choosing.',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 500 }}>
|
||||||
|
<Typography variant="h5" sx={{ mb: 1 }}>
|
||||||
|
Choose your service type
|
||||||
|
</Typography>
|
||||||
|
{options.map((option) => (
|
||||||
|
<Card
|
||||||
|
key={option.id}
|
||||||
|
variant="outlined"
|
||||||
|
interactive
|
||||||
|
selected={selectedId === option.id}
|
||||||
|
padding="compact"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => setSelectedId(option.id)}
|
||||||
|
role="radio"
|
||||||
|
aria-checked={selectedId === option.id}
|
||||||
|
>
|
||||||
|
<Typography variant="labelLg" gutterBottom>
|
||||||
|
{option.title}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
{option.desc}
|
||||||
|
</Typography>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── Multi-Select Pattern ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multi-select variant — click to toggle multiple cards.
|
||||||
|
* Useful for add-on services, package inclusions, etc.
|
||||||
|
*/
|
||||||
|
export const MultiSelect: Story = {
|
||||||
|
name: 'Multi-Select',
|
||||||
|
render: function MultiSelectDemo() {
|
||||||
|
const [selected, setSelected] = useState<Set<string>>(new Set(['flowers']));
|
||||||
|
|
||||||
|
const addOns = [
|
||||||
|
{ id: 'flowers', title: 'Floral arrangements', desc: 'Custom flowers for the service' },
|
||||||
|
{ id: 'catering', title: 'Catering', desc: 'Light refreshments after the service' },
|
||||||
|
{ id: 'music', title: 'Live musician', desc: 'Solo musician for the ceremony' },
|
||||||
|
{ id: 'printing', title: 'Memorial printing', desc: 'Order of service booklets' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const toggle = (id: string) => {
|
||||||
|
setSelected((prev) => {
|
||||||
|
const next = new Set(prev);
|
||||||
|
if (next.has(id)) next.delete(id);
|
||||||
|
else next.add(id);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ maxWidth: 500 }}>
|
||||||
|
<Typography variant="h5" sx={{ mb: 1 }}>
|
||||||
|
Select add-ons
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||||
|
Choose as many as you like
|
||||||
|
</Typography>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||||
|
{addOns.map((item) => (
|
||||||
|
<Card
|
||||||
|
key={item.id}
|
||||||
|
variant="outlined"
|
||||||
|
interactive
|
||||||
|
selected={selected.has(item.id)}
|
||||||
|
padding="compact"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => toggle(item.id)}
|
||||||
|
role="checkbox"
|
||||||
|
aria-checked={selected.has(item.id)}
|
||||||
|
>
|
||||||
|
<Typography variant="labelLg" gutterBottom>
|
||||||
|
{item.title}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
{item.desc}
|
||||||
|
</Typography>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── On Different Backgrounds ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates how cards adapt to different surface colours.
|
||||||
|
* Elevated cards stand out via shadow on any surface.
|
||||||
|
* Outlined cards use borders on white, contrast on grey.
|
||||||
|
*/
|
||||||
|
export const OnDifferentBackgrounds: Story = {
|
||||||
|
name: 'On Different Backgrounds',
|
||||||
|
render: () => (
|
||||||
|
<div style={{ display: 'flex', gap: 32, maxWidth: 900 }}>
|
||||||
|
{/* White background */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
padding: 24,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
borderRadius: 8,
|
||||||
|
border: '1px dashed #d4d4d4',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ mb: 2, display: 'block' }}>
|
||||||
|
On white surface
|
||||||
|
</Typography>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
|
<Card variant="elevated">
|
||||||
|
<Typography variant="labelLg">Elevated</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">Shadow defines edges</Typography>
|
||||||
|
</Card>
|
||||||
|
<Card variant="outlined">
|
||||||
|
<Typography variant="labelLg">Outlined</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">Border defines edges</Typography>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Grey background */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
padding: 24,
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
borderRadius: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ mb: 2, display: 'block' }}>
|
||||||
|
On grey surface
|
||||||
|
</Typography>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
|
<Card variant="elevated">
|
||||||
|
<Typography variant="labelLg">Elevated</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">White card + shadow on grey</Typography>
|
||||||
|
</Card>
|
||||||
|
<Card variant="outlined">
|
||||||
|
<Typography variant="labelLg">Outlined</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">Contrast + border on grey</Typography>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
// ─── Padding Presets ────────────────────────────────────────────────────────
|
// ─── Padding Presets ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** All three padding options */
|
/** All three padding options */
|
||||||
@@ -190,41 +406,6 @@ export const PriceCardPreview: Story = {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ─── Service Option Preview ─────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preview of how Card will be used in the ServiceOption molecule.
|
|
||||||
* Shows a selectable option card pattern.
|
|
||||||
*/
|
|
||||||
export const ServiceOptionPreview: Story = {
|
|
||||||
name: 'Service Option Preview',
|
|
||||||
render: () => (
|
|
||||||
<div style={{ display: 'flex', gap: 16, maxWidth: 700 }}>
|
|
||||||
{[
|
|
||||||
{ title: 'Chapel service', desc: 'Traditional ceremony in our chapel' },
|
|
||||||
{ title: 'Graveside service', desc: 'Intimate outdoor farewell' },
|
|
||||||
{ title: 'Memorial service', desc: 'Celebration of life gathering' },
|
|
||||||
].map((option) => (
|
|
||||||
<Card
|
|
||||||
key={option.title}
|
|
||||||
variant="outlined"
|
|
||||||
interactive
|
|
||||||
padding="compact"
|
|
||||||
tabIndex={0}
|
|
||||||
sx={{ flex: 1 }}
|
|
||||||
>
|
|
||||||
<Typography variant="labelLg" gutterBottom>
|
|
||||||
{option.title}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
{option.desc}
|
|
||||||
</Typography>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
// ─── With Image ─────────────────────────────────────────────────────────────
|
// ─── With Image ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Card with full-bleed image using padding="none" */
|
/** Card with full-bleed image using padding="none" */
|
||||||
@@ -260,7 +441,7 @@ export const WithImage: Story = {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ─── Nested Content ─────────────────────────────────────────────────────────
|
// ─── Rich Content ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Card with rich nested content to verify layout flexibility */
|
/** Card with rich nested content to verify layout flexibility */
|
||||||
export const RichContent: Story = {
|
export const RichContent: Story = {
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import CardContent from '@mui/material/CardContent';
|
|||||||
export interface CardProps extends Omit<MuiCardProps, 'raised' | 'variant'> {
|
export interface CardProps extends Omit<MuiCardProps, 'raised' | 'variant'> {
|
||||||
/** Visual style: "elevated" uses shadow, "outlined" uses border */
|
/** Visual style: "elevated" uses shadow, "outlined" uses border */
|
||||||
variant?: 'elevated' | 'outlined';
|
variant?: 'elevated' | 'outlined';
|
||||||
/** Adds hover shadow lift and pointer cursor for clickable cards */
|
/** Adds hover background fill, shadow lift, and pointer cursor for clickable cards */
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
|
/** Highlights the card as selected — brand border + warm background tint */
|
||||||
|
selected?: boolean;
|
||||||
/** Padding preset: "default" (24px), "compact" (16px), "none" (no wrapper) */
|
/** Padding preset: "default" (24px), "compact" (16px), "none" (no wrapper) */
|
||||||
padding?: 'default' | 'compact' | 'none';
|
padding?: 'default' | 'compact' | 'none';
|
||||||
/** Content to render inside the card */
|
/** Content to render inside the card */
|
||||||
@@ -23,14 +25,17 @@ export interface CardProps extends Omit<MuiCardProps, 'raised' | 'variant'> {
|
|||||||
* Content container for the FA design system.
|
* Content container for the FA design system.
|
||||||
*
|
*
|
||||||
* Wraps MUI Card with FA brand tokens, two visual variants (elevated/outlined),
|
* Wraps MUI Card with FA brand tokens, two visual variants (elevated/outlined),
|
||||||
* optional hover interactivity, and padding presets.
|
* optional hover interactivity, selected state, and padding presets.
|
||||||
*
|
*
|
||||||
* Variant mapping from design:
|
* Variant mapping from design:
|
||||||
* - `elevated` (default) — shadow.md resting, white background
|
* - `elevated` (default) — shadow.md resting, white background
|
||||||
* - `outlined` — neutral border, no shadow, white background
|
* - `outlined` — neutral border, no shadow, white background
|
||||||
*
|
*
|
||||||
* Use `interactive` for clickable cards (PriceCard, ServiceOption) —
|
* Use `interactive` for clickable cards (PriceCard, ServiceOption) —
|
||||||
* adds shadow.lg hover lift and cursor pointer.
|
* adds background fill on hover, shadow lift, and cursor pointer.
|
||||||
|
*
|
||||||
|
* Use `selected` for option-select patterns — applies brand border
|
||||||
|
* (warm gold) and warm background tint (brand.50).
|
||||||
*
|
*
|
||||||
* Use `padding="none"` when composing with CardMedia or custom layouts
|
* Use `padding="none"` when composing with CardMedia or custom layouts
|
||||||
* that need full-bleed content.
|
* that need full-bleed content.
|
||||||
@@ -40,6 +45,7 @@ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|||||||
{
|
{
|
||||||
variant = 'elevated',
|
variant = 'elevated',
|
||||||
interactive = false,
|
interactive = false,
|
||||||
|
selected = false,
|
||||||
padding = 'default',
|
padding = 'default',
|
||||||
children,
|
children,
|
||||||
sx,
|
sx,
|
||||||
@@ -56,11 +62,21 @@ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|||||||
variant={muiVariant}
|
variant={muiVariant}
|
||||||
elevation={0}
|
elevation={0}
|
||||||
sx={[
|
sx={[
|
||||||
// Interactive: hover lift + pointer
|
// Selected state: brand border + warm background
|
||||||
|
selected && {
|
||||||
|
borderColor: 'var(--fa-card-border-selected)',
|
||||||
|
borderWidth: '2px',
|
||||||
|
borderStyle: 'solid',
|
||||||
|
backgroundColor: 'var(--fa-card-background-selected)',
|
||||||
|
},
|
||||||
|
// Interactive: hover fill + shadow lift + pointer
|
||||||
interactive && {
|
interactive && {
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
boxShadow: 'var(--fa-card-shadow-hover)',
|
backgroundColor: selected
|
||||||
|
? 'var(--fa-card-background-selected)'
|
||||||
|
: 'var(--fa-card-background-hover)',
|
||||||
|
boxShadow: variant === 'elevated' ? 'var(--fa-card-shadow-hover)' : undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Focus-visible for keyboard accessibility on interactive cards
|
// Focus-visible for keyboard accessibility on interactive cards
|
||||||
|
|||||||
@@ -357,5 +357,8 @@
|
|||||||
--fa-typography-overline-sm-font-size: var(--fa-font-size-2xs); /** 11px — accessibility floor */
|
--fa-typography-overline-sm-font-size: var(--fa-font-size-2xs); /** 11px — accessibility floor */
|
||||||
--fa-typography-overline-sm-font-weight: var(--fa-font-weight-semibold);
|
--fa-typography-overline-sm-font-weight: var(--fa-font-weight-semibold);
|
||||||
--fa-card-border-default: var(--fa-color-border-default); /** Neutral border for outlined cards */
|
--fa-card-border-default: var(--fa-color-border-default); /** Neutral border for outlined cards */
|
||||||
|
--fa-card-border-selected: var(--fa-color-border-brand); /** Brand border for selected/active cards — warm gold accent */
|
||||||
--fa-card-background-default: var(--fa-color-surface-raised); /** White — standard card background (raised surface) */
|
--fa-card-background-default: var(--fa-color-surface-raised); /** White — standard card background (raised surface) */
|
||||||
|
--fa-card-background-hover: var(--fa-color-surface-subtle); /** Subtle grey fill on hover — neutral.50 for soft interactive feedback */
|
||||||
|
--fa-card-background-selected: var(--fa-color-surface-warm); /** Warm tint for selected cards — brand.50 reinforces active state */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ export const CardPaddingCompact = "16px"; // 16px — compact card padding (mobi
|
|||||||
export const CardShadowDefault = "0 4px 6px rgba(0,0,0,0.07)"; // Medium shadow — resting elevated card
|
export const CardShadowDefault = "0 4px 6px rgba(0,0,0,0.07)"; // Medium shadow — resting elevated card
|
||||||
export const CardShadowHover = "0 10px 15px rgba(0,0,0,0.1)"; // High shadow — interactive card on hover
|
export const CardShadowHover = "0 10px 15px rgba(0,0,0,0.1)"; // High shadow — interactive card on hover
|
||||||
export const CardBorderDefault = "#e8e8e8"; // Neutral border for outlined cards
|
export const CardBorderDefault = "#e8e8e8"; // Neutral border for outlined cards
|
||||||
|
export const CardBorderSelected = "#ba834e"; // Brand border for selected/active cards — warm gold accent
|
||||||
export const CardBackgroundDefault = "#ffffff"; // White — standard card background (raised surface)
|
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 InputHeightSm = "40px"; // Small — compact forms, admin layouts, matches Button medium height
|
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 InputHeightMd = "48px"; // Medium (default) — standard forms, matches Button large for alignment
|
||||||
export const InputPaddingXDefault = "12px"; // 12px — inner horizontal padding matching Figma design
|
export const InputPaddingXDefault = "12px"; // 12px — inner horizontal padding matching Figma design
|
||||||
|
|||||||
@@ -19,13 +19,16 @@
|
|||||||
},
|
},
|
||||||
"border": {
|
"border": {
|
||||||
"$type": "color",
|
"$type": "color",
|
||||||
"$description": "Border colours for the outlined card variant.",
|
"$description": "Border colours for card variants and states.",
|
||||||
"default": { "$value": "{color.border.default}", "$description": "Neutral border for outlined cards" }
|
"default": { "$value": "{color.border.default}", "$description": "Neutral border for outlined cards" },
|
||||||
|
"selected": { "$value": "{color.border.brand}", "$description": "Brand border for selected/active cards — warm gold accent" }
|
||||||
},
|
},
|
||||||
"background": {
|
"background": {
|
||||||
"$type": "color",
|
"$type": "color",
|
||||||
"$description": "Card background colours.",
|
"$description": "Card background colours.",
|
||||||
"default": { "$value": "{color.surface.raised}", "$description": "White — standard card background (raised surface)" }
|
"default": { "$value": "{color.surface.raised}", "$description": "White — standard card background (raised surface)" },
|
||||||
|
"hover": { "$value": "{color.surface.subtle}", "$description": "Subtle grey fill on hover — neutral.50 for soft interactive feedback" },
|
||||||
|
"selected": { "$value": "{color.surface.warm}", "$description": "Warm tint for selected cards — brand.50 reinforces active state" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user