From a9f62b2d2b4781b8053d79ec0f2a41f080684a69 Mon Sep 17 00:00:00 2001 From: Parth Gartan Date: Sat, 12 Sep 2026 08:51:40 +0000 Subject: [PATCH 1/4] make DESIGN.md an AI-ready Sistent design contract Signed-off-by: Parth Gartan --- DESIGN.md | 355 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 297 insertions(+), 58 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 451356b49..10481a044 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -2,6 +2,8 @@ version: alpha name: Sistent description: Open-source dashboard and component-system visual identity with a neutral application shell, teal brand actions, saffron CTAs, and a first-class dark mode. +target_framework: "@mui/material + React + TypeScript" +package_source_of_truth: package.json colors: primary: '#00B39F' on-primary: '#FDFDFD' @@ -22,7 +24,7 @@ colors: surface-light-tabs: '#F6F8F8' on-surface-light: '#000D12' surface-dark-app: '#000D12' - surface-dark-card: '#121212' + surface-dark-card: '#212121' surface-dark-muted: '#15272F' surface-dark-tabs: '#1A1A1A' on-surface-dark: '#FDFDFD' @@ -298,94 +300,331 @@ components: padding: 4px 8px --- -## Overview +## 1. Overview & Architectural Boundaries -Sistent presents itself as a practical open-source product system for dashboards, admin surfaces, and data-heavy workflows. The visual identity is calm and technical rather than decorative: a cool neutral shell carries the interface, a vivid teal handles primary interaction, saffron is reserved for CTA or status emphasis, and dark mode is treated as a real peer instead of an afterthought. +Sistent is the design system and UI component library for Layer5 products and open-source applications (such as Meshery). It provides a calm, technical visual identity built around: +- A cool, restrained neutral application shell. +- Vivid brand teal (`#00B39F`) for primary actions, navigation indicators, and active cues. +- Saffron (`#EBC017`) as an intentional CTA and badge accent. +- A first-class dark mode treated as a peer surface system rather than a naive color inversion. -The design language blends Material UI's structural discipline with a few unmistakable Layer5 signatures: blue-gray gradient chrome, a faint radial wash on cards, accent-tinted hover depth on showcase surfaces, and a teal luminescent tooltip shadow in dark mode. The result feels reliable, community-built, and slightly optimistic without becoming playful or glossy. +### Document Scope & Separation of Concerns -## Colors +To avoid contradictory guidance across the repository, documentation responsibilities are strictly separated: -The palette is anchored by restrained neutrals. In light mode, the application canvas is near-white with gentle charcoal steps for borders, tabs, and secondary surfaces. In dark mode, those same layers invert into deep charcoal and blue-black slabs with softened gray dividers instead of pure black-on-black contrast. +* **`DESIGN.md` (UI & Design Contract)**: + * **Owns**: Visual philosophy, semantic design tokens (frontmatter), verified token-to-code bridges, component conventions, interaction states, responsive layouts, evidence-backed accessibility guidance, and UI-specific AI guardrails. + * **Strict Non-Goals**: Build tooling, rollup/dts configurations, npm releases, package dependency categorization, multi-repo schema definitions, and git commit signing (DCO). +* **`AGENTS.md` (Repository & Engineering Contract)**: + * **Owns**: Contributor engineering runbooks, build/test gates, declaration bundling troubleshooting, optional peer rules, schema derivation contracts, and DCO requirements. + * **Strict Non-Goals**: Visual styling choices, typography variants, component color palettes, and UI layout rules. +* **`package.json`**: + * **Sole Authority** for installed framework and library versions (e.g. `@mui/material`, `react`, `typescript`). -- **Brand teal** is the primary interactive color. Use it for contained buttons, active states, verification cues, and brand-positive emphasis. -- **Saffron** is a secondary accent. Use it more sparingly for CTAs, official badges, and graphic emphasis so it remains noticeable. -- **Navigation chrome** is darker and cooler than content surfaces. Even in light mode, nav bars and drawers sit in blue-gray territory rather than white. -- **Status colors** are intentionally distinct from the brand color so alerts, warnings, and errors remain legible inside data-heavy interfaces. +--- + +## 2. The Two-Layer Model: Design Tokens vs Runtime MUI Theme + +> ### Core Principle: DESIGN TOKENS ≠ MUI THEME API +> Sistent establishes an explicit two-layer design contract: +> 1. **Layer 1: Semantic Design Tokens**: Abstract design concepts defined in the YAML frontmatter (`colors.primary`, `spacing.lg`, `rounded.sm`). These define visual relationships, scales, and component compositions across design tools and specifications. +> 2. **Layer 2: Canonical Runtime Implementation**: How React and TypeScript code consumes those tokens via Material UI (`theme.palette.interactive.primary`, `theme.spacing(2)`, `theme.shape.borderRadius`). +> +> **AI Coding Rule**: Never write design tokens as literal runtime theme properties (e.g. do not write `theme.spacing.lg`, `theme.rounded.sm`, or `theme.shadows['card-accent']`). Instead, use the explicit mappings below to translate design tokens into their canonical runtime implementations. + +--- + +## 3. Explicit Design-Token-to-Runtime-Implementation Mappings + +Every documented implementation path below is verified against current repository source code or explicitly identified as a historical/compatibility reference. + +### A. Colors & Surface Semantics + +> **Surface Semantics Rule**: Do not assume `theme.palette.background.card` is the universal token for all surfaces. Sistent distinguishes between application canvases, cards, modal bodies, panels, and data surfaces based on their structural role. + +| Design Token | Design Value (Light / Dark) | Semantic Role | Canonical Implementation Path | Classification | New Code Guidance | +| :--- | :--- | :--- | :--- | :--- | :--- | +| `colors.primary` | `#00B39F` / `#00B39F` | Interactive Primary | `theme.palette.interactive.primary` | Canonical Semantic | **Use for all new interactive controls, icons, and active indicators.** | +| `colors.primary-hover` | `#41CCB3` / `#41CCB3` | Interactive Hover | `theme.palette.interactive.hover` | Canonical Semantic | **Use for hover states of primary interactive controls.** | +| `colors.primary-pressed`| `#93E6D1` / `#93E6D1` | Interactive Pressed | `theme.palette.interactive.pressed` | Canonical Semantic | **Use for active and pressed control states.** | +| `colors.secondary` | `#3C494F` / `#B1B9BC` | Secondary Text / Icon | `theme.palette.text.secondary` / `icon.secondary` | Canonical Semantic | **Use for captions, metadata, hints, and passive icons.** | +| `colors.accent` | `#EBC017` / `#EBC017` | CTA Accent (Saffron) | `theme.palette.background.cta?.default` | Active Component Binding | **Use for high-visibility Call-to-Action buttons.** | +| `colors.accent-hover` | `#FFEB6B` / `#FFEB6B` | CTA Hover | `theme.palette.background.cta?.hover` | Active Component Binding | **Use for hover states of CTA buttons.** | +| `colors.navigation-light / dark`| `#252E31` / `#000D12`| Navigation Bar | `theme.palette.navigation.primary` | Canonical Semantic | **Use for application shell navigation bars and side drawers.** | +| `colors.surface-light / dark-app`| `#FDFDFD` / `#000D12`| Surface Canvas (App) | `theme.palette.surface.primary` | Canonical Semantic | **Use for application screen canvases and modal body containers.** | +| `colors.surface-light / dark-card`| `#FFFFFF` / `#212121`| Card Container | `theme.palette.background.card` | Active Component Binding | Used for card surfaces and dark-mode table toolbars/headers. | +| `colors.surface-light / dark-muted`| `#F6F8F8` / `#15272F`| Panel / Data Surface | `theme.palette.background.surfaces` | Active Component Binding | Used for floating panel bodies, sidebars, and light-mode table headers. | +| `colors.surface-light / dark-tabs`| `#F6F8F8` / `#1A1A1A`| Tabs Background | `theme.palette.background.tabs` | Active Component Binding | Used for segmented tab container bars (`tab.modifier.ts`). | +| `strokes.light / dark-default`| `#EAEDEE` / `#15272F`| Border Default | `theme.palette.border.default` | Canonical Semantic | **Use for standard card outlines and container dividers.** | +| `strokes.light / dark-strong`| `#28353A` / `#8D9FA7`| Border Strong | `theme.palette.border.strong` | Canonical Semantic | **Use for emphasized borders and active boundaries.** | +| `strokes.light / dark-normal`| `#8C999E` / `#3D4F57`| Border Normal | `theme.palette.border.normal` | Canonical Semantic | **Use for intermediate divider contrast.** | +| `icons.light / dark-primary`| `#15272F` / `#D2D8DA`| Primary Icon Fill | `theme.palette.icon.default` | Canonical Semantic | **Use for standard icons.** | +| `status-colors.info` | `#2196F3` / `#2196F3` | System Info | `theme.palette.status.info` | Canonical Semantic | **Use for informative alerts and status badges.** | +| `status-colors.success`| `#206D24` / `#206D24` | System Success | `theme.palette.status.success` | Canonical Semantic | **Use for success states and confirmation badges.** | +| `status-colors.warning`| `#F0A303` / `#F0A303` | System Warning | `theme.palette.status.warning` | Canonical Semantic | **Use for warnings and caution alerts.** | +| `status-colors.error` | `#F91313` / `#F91313` | System Error | `theme.palette.status.error` | Canonical Semantic | **Use for errors, alert banners, and destructive actions.** | +| `gradients.tint-light / dark`| Gradient | Surface Header Tint | `theme.palette.surface.tint` | Canonical Semantic | **Use for modal, drawer, and panel header gradient banners.** | +| — | — | App Canvas (Legacy) | `theme.palette.background.default` | Legacy / Compatibility | Baseline MUI background; prefer `surface.primary` in new code. | +| — | — | MUI Primary Main | `theme.palette.primary.main` | Deprecated | Avoid direct theme path in new code; pass `color="primary"` to Sistent components. | +| — | — | Text Brand | `theme.palette.text.brand` | Deprecated | Avoid in new code; use `theme.palette.interactive.primary`. | + +--- + +### B. Spacing: Semantic Tokens vs Functional Invocation + +> **AI Rule**: `spacing.*` tokens define the design grid rhythm. At runtime, the MUI theme implements this via an invocable function `theme.spacing(factor)`. Never write `theme.spacing.base` or `theme.spacing.lg` in code. + +| Design Token | Design Value | Canonical Runtime Implementation | Usage Context | +| :--- | :--- | :--- | :--- | +| `spacing.xxs` | `2px` | `theme.spacing(0.25)` or `2px` literal | Micro gaps, border adjustments | +| `spacing.xs` | `4px` | `theme.spacing(0.5)` | Micro gaps, tight chip padding | +| `spacing.sm` / `spacing.base` | `8px` | `theme.spacing(1)` | Base element separation | +| `spacing.md` | `12px` | `theme.spacing(1.5)` | Compact card gutters, internal control padding | +| `spacing.lg` | `16px` | `theme.spacing(2)` | Standard container padding, button horizontal padding | +| `spacing.xl` | `20px` | `theme.spacing(2.5)` | Modal and panel padding | +| `spacing.xxl` | `24px` | `theme.spacing(3)` | Section separation | +| `spacing.xxxl` | `32px` | `theme.spacing(4)` | Major layout gutters | + +--- + +### C. Border Radii: Semantic Tokens vs Theme Shapes & Conventions -## Typography +> **AI Rule**: Sistent defines a single formal theme token: `theme.shape.borderRadius` (`4px`). Other radii in the `rounded.*` token scale represent component-level CSS conventions. Sistent does not define a `theme.rounded` object. -Sistent's hierarchy is built around **Qanelas Soft Regular** for most UI copy and headings, with **Open Sans** appearing in supporting body and label roles, and **Consolas** reserved for code-styled content. The overall tone is soft enough to feel approachable, but still structured enough for operational interfaces. +| Design Token | Design Value | Canonical Implementation Mechanism | Component Conventions | +| :--- | :--- | :--- | :--- | +| `rounded.xs` | `2px` | CSS literal (`2px`) | Micro controls (checkboxes, inner tags) | +| `rounded.sm` | `4px` | `theme.shape.borderRadius` | Standard controls (Buttons, TextFields, base Cards) | +| `rounded.md` | `5px` | CSS literal (`5px` / `0.5rem`) | Modal dialog wrappers, header caps | +| `rounded.lg` | `8px` | CSS literal (`8px` / `0.5rem`) | Modal dialog containers, floating panels | +| `rounded.xl` | `15px` | CSS literal (`15px`) | Featured card variants | +| `rounded.xxl` | `16px` | CSS literal (`16px`) | Catalog showcase cards | +| `rounded.full` | `9999px` | CSS literal (`9999px`) | Badges, rounded pills, avatars | -- **Headings** are large and clean, with medium-to-bold weights and compact tracking. -- **Body copy** uses a generous `1.75rem` line height, which keeps settings screens, tables, and instructional content readable. -- **Buttons and active labels** lean semi-bold and capitalize words rather than shouting in full uppercase. -- **Code treatments** are small, utilitarian, and intentionally plain. +--- + +### D. Typography System -On smaller screens, the largest heading sizes step down rather than reflowing into dramatic mobile-specific styles. The system preserves hierarchy, but avoids oversized hero typography. +All 10 custom variants are registered in `MuiTypography` via `src/theme/typography.ts`. Consumer code must use these named variants rather than declaring arbitrary font styles: -## Layout & Spacing +| Design Token | Typography Spec | Canonical Runtime Usage | Responsive Behavior (`down('sm')`) | Primary Role | +| :--- | :--- | :--- | :--- | :--- | +| `typography.textH1Bold` | `3.25rem` / `4rem`, 700 | `` | Scales to `2rem` / `2.5rem` | Top-level screen headers | +| `typography.textH2Medium` | `2rem` / `2.5rem`, 500 | `` | Scales to `1.5rem` / `2.25rem` | Primary section headers | +| `typography.textH3Medium` | `1.5rem` / `2.25rem`, 500 | `` | Scales to `1rem` / `1.75rem` (700) | Card & modal titles | +| `typography.textB1Regular` | `1rem` / `1.75rem`, 400 | `` | Preserves `1rem` / `1.75rem` | Primary UI & body copy | +| `typography.textB2SemiBold`| `1rem` / `1.75rem`, 600, `capitalize` | `` | Preserves `1rem` / `1.75rem` | Action labels, active tab titles | +| `typography.textB3Regular` | `0.875rem` / `1.5rem`, 400 | `` | Fixed | Supporting labels, hints | +| `typography.textL1Bold` | `0.75rem` / `1rem`, 700 | `` | Fixed | Ribbon badges, small chips | +| `typography.textL2Regular` | `0.75rem` / `1.5rem`, 400 | `` | Fixed | Secondary metadata, captions | +| `typography.textC1Regular` | `0.75rem` / `1.5rem`, 400 | `` | Fixed | Compact code blocks, UUIDs | +| `typography.textC2Regular` | `1rem` / `1.75rem`, 400 | `` | Fixed | Standard monospace text | + +--- -Spacing follows a clear **8px grid**. The most common working values are `16px`, `20px`, and `24px`, with `12px` used for more compact interactive padding and `32px` reserved for larger layout separation. +### E. Shadows, Elevation & Depth + +* **Tooltip Elevation**: Encapsulated in `src/theme/components/tooltip.modifier.ts`. Automatically renders neutral shadow in light mode and a dual-tone teal luminescent glow in dark mode. +* **Card Surface Depth**: Encapsulated via the subtle radial wash in `src/theme/components/card.modifier.ts`. +* **Floating Panels**: Composed using backdrop blur: `boxShadow: 0 4px 16px ${theme.palette.background.blur?.light}` (`src/custom/Panel/style.tsx`). +* **Showcase Cards**: Accent shadow: `boxShadow: 2px 2px 3px 0px ${theme.palette.background.brand?.default}` (`src/custom/CatalogCard/style.tsx`). +* **Negative Constraint**: `theme.shadows` is a standard MUI 25-element tuple. Named keys like `theme.shadows['card-accent']` or `theme.elevation` do not exist. + +--- -The system generally prefers compact, information-dense arrangement over expansive marketing whitespace. That said, it avoids feeling cramped by keeping horizontal padding predictable and by separating structural chrome from content with tonal layers instead of large gutters. +## 4. Component Usage & Import Conventions -- **Buttons and tabs** commonly use `16px` horizontal padding. -- **Modal headers** and action bars land on `11px 16px` and `20px 16px` style rhythms. -- **Panels and cards** stay tidy; they read as tools, not canvas art. +### Import Precedence Rule +> **"Prefer `@sistent/sistent` when a Sistent abstraction exists. Use `@mui/material` when no equivalent exists or when implementing Sistent itself."** -## Elevation & Depth +* **Base Controls**: Always import from `@sistent/sistent`: + `Button`, `Card`, `Checkbox`, `Dialog`, `IconButton`, `Select`, `Tab`, `Tabs`, `TextField`, `Tooltip`, `Typography`. +* **Custom Components**: Always import from `@sistent/sistent`: + `ActionButton`, `CustomTooltip`, `Modal`, `Panel`, `ResponsiveDataTable`, `SearchBar`, `UniversalFilter`. +* **MUI Fallbacks**: Permitted only when building internal Sistent primitives or when no Sistent wrapper exists. -Depth in Sistent is subtle. Most surfaces separate through **tone**, **border**, and **surface step**, not through large shadows. This keeps tables, cards, and forms feeling crisp and controlled. +--- -- **Standard cards** use a very faint radial wash rather than dramatic dropshadow. -- **Interactive showcase cards** lift slightly on hover and gain a soft shadow or teal-tinted accent shadow. -- **Floating panels** rely on semi-opaque blurred surfaces plus a modest `0 4px 16px` shadow. -- **Tooltips** are the most expressive surface in the system: neutral shadow in light mode, teal glow in dark mode. +## 5. Interaction States Matrix -When elevation appears, it should feel functional and technical, not plush. +Interaction states are derived from active Sistent component implementations and theme modifiers (`button.modifier.ts`, `tab.modifier.ts`, `input.modifier.ts`): -## Shapes +| Interaction State | Primary Contained Control | Outlined Control | Surface / Container | Rule Status | +| :--- | :--- | :--- | :--- | :--- | +| **Default** | `theme.palette.interactive.primary` | Transparent background, `border.default` | Canonical surface token (`surface.primary`, `background.card`) | Canonical Design Rule | +| **Hover** | `theme.palette.interactive.hover` (`#41CCB3`) | `theme.palette.background.hover` | `theme.palette.background.hover` | Canonical Design Rule | +| **Pressed** | `theme.palette.interactive.pressed` (`#93E6D1`) | `theme.palette.interactive.pressed` tint | Stepped surface depth | Canonical Design Rule | +| **Focus-Visible** | Distinct focus outline: `theme.palette.border.brand` | Outline: `theme.palette.border.brand` | Outline: `theme.palette.border.brand` | Canonical Design Rule | +| **Disabled** | `theme.palette.interactive.disabled`, text `disabled` | Stroke `theme.palette.text.disabled` | N/A | Canonical Design Rule | +| **Loading** | Spinner / skeleton indicator; interactions inert | Inherits disabled state modifiers | Loading overlay / skeleton | Implementation-Specific | +| **Selected / Active** | Active tab indicator / contained toggle | `border.brand` with subtle background tint | Component-specific active fill | Implementation-Specific | +| **Error** | Background: `theme.palette.background.error?.default`, text via `getContrastText()` | Border: `theme.palette.status.error` | Border: `theme.palette.status.error` | Canonical Design Rule | +| **Success / Warning** | Component-specific status badge / icon | Border / text using status token | Background / border using status token | Implementation-Specific | -The shape language is mostly rectangular with measured softening. The default rhythm still feels MUI-like: many base components sit at a `4px` radius, while a few custom shells push slightly further. +--- -- **Default controls and cards:** `4px` -- **Modal wrappers and gradient headers:** `5px` -- **Floating panels and image wells:** `8px` -- **Feature and catalog cards:** `15px` to `16px` -- **Micro-controls like checkboxes:** `2px` +## 6. Authorization & Permissions -Use larger radii as a signal that a surface is more editorial, showcase-oriented, or independently draggable. Do not round everything equally. +### Permission Rule +> **"When an action is authorization-controlled, use the existing Sistent permission mechanism and provide the appropriate `permissionKey`. Do not invent local permission logic."** -## Components +* Built-in native support exists on: `Button`, `IconButton`, `MenuItem`, `ListItem`, and `ListItemButton`. +* If unauthorized, the component automatically disables itself and shows a badge tooltip (`permissionAction="showShield"`, default) or renders nothing (`permissionAction="hide"`). +* **Applicability Scope**: Actions that are not authorization-controlled and standard informational triggers do **not** require `permissionKey`. The optional `permissionKey` prop is strictly for protected operations. +* Arbitrary custom triggers must be wrapped in ``. -### Buttons +--- -Contained buttons are solid teal with white text and semi-bold labels. Outlined buttons are intentionally spare: neutral strokes, transparent fill, and color that adapts cleanly across light and dark surfaces. +## 7. Responsive Behavior & Accessibility (a11y) Guidance -### Tabs +* **Breakpoints**: Standard MUI breakpoints (`xs: 0`, `sm: 600px`, `md: 900px`, `lg: 1200px`, `xl: 1536px`). +* **Header Auto-Scaling**: `textH1Bold`, `textH2Medium`, and `textH3Medium` automatically scale down below the `sm` breakpoint via `src/theme/typography.ts`. +* **Dynamic Contrast & Readability**: + * **Mechanism**: Use `readableTextColor(bg)` from `src/theme/theme.ts` when placing text over dynamic brand/custom background fills to automatically select high-contrast ink (`charcoal[10]` vs `charcoal[100]`). + * **Design Guidance**: Aim for 4.5:1 contrast on standard text and 3:1 on large text/icons against their immediate surface. + * **Enforcement Status**: Contrast is guided by theme tokens and helper utilities (`readableTextColor`), but is not enforced by a global automated linting/test suite across every component. +* **Label Capitalization**: Action labels use `textTransform: 'capitalize'` built into `textB2SemiBold`. +* **Semantic ARIA**: Icon-only buttons must supply descriptive `aria-label` and `Tooltip`. -Tabs use flat segmented surfaces rather than pills. The selected tab sits on a dedicated tab background and is reinforced by a brand-colored indicator, which keeps navigation clear without excessive ornament. +--- -### Inputs and Checks +## 8. Canonical Code Recipes + +All code recipes below adhere strictly to Sistent component conventions, canonical semantic tokens, accessibility guidelines, and AI guardrails: + +### Recipe 1: Container Surface with Light/Dark Support +```tsx +import React from 'react'; +import { styled, Box, Typography } from '@sistent/sistent'; + +const CardContainer = styled(Box)(({ theme }) => ({ + backgroundColor: theme.palette.background.card, + color: theme.palette.text.default, + border: `1px solid ${theme.palette.border.default}`, + borderRadius: theme.shape.borderRadius, + padding: theme.spacing(2), +})); + +export const CustomCard: React.FC<{ title: string; children: React.ReactNode }> = ({ title, children }) => ( + + + {title} + + theme.spacing(1.5) }}> + {children} + + +); +``` + +### Recipe 2: Authorization-Controlled Action Button + +> **Note**: This pattern applies strictly to actions that are actually authorization-controlled. The optional `permissionKey` must not be interpreted as a requirement for every button or action in the system; standard actions and informational triggers should omit `permissionKey`. + +```tsx +import React from 'react'; +import { Button } from '@sistent/sistent'; +import type { Key } from '@meshery/schemas/permissions'; + +interface ActionProps { + label: string; + onClick: () => void; + permissionKey?: Key; +} + +export const ActionButton: React.FC = ({ label, onClick, permissionKey }) => ( + +); +``` + +### Recipe 3: Modal Dialog with Sistent Gradient Header +```tsx +import React from 'react'; +import { styled, Dialog, DialogContent, Box, Typography } from '@sistent/sistent'; + +const ModalHeader = styled(Box)(({ theme }) => ({ + background: theme.palette.surface.tint, + color: theme.palette.text.constant?.white, + padding: theme.spacing(2), + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', +})); + +const ModalBody = styled(DialogContent)(({ theme }) => ({ + backgroundColor: theme.palette.surface.primary, + padding: theme.spacing(2.5), +})); + +export const SistentModal: React.FC<{ + open: boolean; + title: string; + onClose: () => void; + children: React.ReactNode; +}> = ({ open, title, onClose, children }) => ( + + + {title} + + + {children} + + +); +``` -Text inputs are understated. Their identity comes mostly from border treatment and focus color, not from filled backgrounds. Checkboxes are squared-off and compact, reinforcing the design system's utilitarian feel. +--- -### Cards, Tables, and Panels +## 9. AI Coding Guardrails (UI & Styling Specific) + +1. **Design Tokens ≠ Runtime Theme API**: + > Translate semantic tokens into canonical runtime calls (`theme.spacing(2)`, `theme.shape.borderRadius`). Never write `theme.spacing.lg` or `theme.rounded.sm`. +2. **Color Literal Rule**: + > Consumer/UI code must not introduce new raw color literals when an existing semantic Sistent token applies. Theme source and token definitions are exempt. +3. **Spacing & Shape Token Rule**: + > Consumer/UI code must use `theme.spacing(n)` for element layout and gutters. Do not invent arbitrary inline pixel offsets when an 8px grid factor satisfies the requirement. +4. **Component Import Rule**: + > Prefer `@sistent/sistent` when a Sistent abstraction exists. Use `@mui/material` when no equivalent exists or when implementing Sistent itself. +5. **Permission Rule**: + > When an action is authorization-controlled, use the existing Sistent permission mechanism and provide the appropriate `permissionKey`. Do not invent local permission logic. +6. **No Manual Dark-Mode Forking**: + > Avoid manual conditionals like `theme.palette.mode === 'dark' ? '#212121' : '#fff'`. Use semantic tokens (`theme.palette.background.card`, `theme.palette.surface.primary`, `theme.palette.text.default`) that resolve automatically. +7. **Preserve Typography Variants**: + > Use `` through `` rather than ad-hoc inline font declarations. -Standard cards and data tables stay quiet, bright, and neutral in light mode, then shift to charcoal slabs in dark mode. Floating panels and modal chrome are where the system becomes more expressive: blurred backgrounds, cool blue-gray gradients, and stronger contrast between header, body, and action areas. +--- -### Catalog and Learning Surfaces +## 10. Documentation / Implementation Drift Protocol -The more editorial custom components introduce a bit more motion and softness: slightly larger radii, hover lift, accent shadows, and corner ribbons for classification. These are still grounded in the same teal-gray palette, so they feel related to the core admin surfaces. +When `DESIGN.md` and repository source code disagree: +1. **Do not silently change the design contract.** +2. **Determine discrepancy classification**: + * **Documentation Drift**: The source implementation was updated intentionally, but `DESIGN.md` was not updated to match (e.g. `surface-dark-card` frontmatter was `#121212` vs code `#212121`). + * **Implementation Drift**: A component diverges from established design tokens due to an accidental or ad-hoc local override. + * **Intentional Legacy / Compatibility Behavior**: An older token path maintained to avoid breaking consumer downstream imports (e.g. `background.default`). + * **Intentional Exception**: A component has a specialized visual requirement that intentionally diverges from the standard token. +3. **Record the discrepancy in the audit log.** +4. **Only change the design contract when there is an explicit design-system decision to do so.** +5. **Do not make unrelated design changes during AI-context work.** -## Do's and Don'ts +--- -- **Do** keep teal as the default primary interaction color. -- **Do** reserve gradients for modal headers, panel chrome, and other structural framing surfaces. -- **Do** prefer tonal layering, border contrast, and subtle depth before adding large shadows. -- **Do** treat dark mode as a fully supported surface system, not a simple inversion. -- **Don't** flood content areas with saturated fills. -- **Don't** overuse large radii on utilitarian controls. -- **Don't** replace neutral text with accent colors unless the content is genuinely interactive or status-bearing. -- **Don't** make tables, forms, or list rows feel decorative; Sistent works best when dense interfaces stay disciplined. +## 11. Do's and Don'ts + +* **Do** keep teal (`#00B39F`) as the default primary interaction color. +* **Do** reserve gradients (`surface.tint`) for modal headers, drawer banners, and structural framing. +* **Do** prefer tonal layering, border contrast, and subtle surface depth before adding large shadows. +* **Do** treat dark mode as a fully supported surface system, not a simple color inversion. +* **Do** use `theme.spacing(factor)` for all layout margins and paddings. +* **Don't** flood content areas with saturated fills. +* **Don't** overuse large radii on utilitarian controls; reserve `16px` for showcase cards. +* **Don't** replace neutral text with accent colors unless the content is genuinely interactive or status-bearing. +* **Don't** write phantom theme paths like `theme.spacing.lg` or `theme.rounded.sm`. From 583147b9c8c12f9e141533253be002cbf3a98cc7 Mon Sep 17 00:00:00 2001 From: Parth Gartan Date: Sat, 12 Sep 2026 18:54:58 +0000 Subject: [PATCH 2/4] docs: address review and bot feedback on design contract Signed-off-by: Parth Gartan --- DESIGN.md | 422 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 243 insertions(+), 179 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 10481a044..16854f9e2 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -2,7 +2,7 @@ version: alpha name: Sistent description: Open-source dashboard and component-system visual identity with a neutral application shell, teal brand actions, saffron CTAs, and a first-class dark mode. -target_framework: "@mui/material + React + TypeScript" +target_framework: '@mui/material + React + TypeScript' package_source_of_truth: package.json colors: primary: '#00B39F' @@ -303,6 +303,7 @@ components: ## 1. Overview & Architectural Boundaries Sistent is the design system and UI component library for Layer5 products and open-source applications (such as Meshery). It provides a calm, technical visual identity built around: + - A cool, restrained neutral application shell. - Vivid brand teal (`#00B39F`) for primary actions, navigation indicators, and active cues. - Saffron (`#EBC017`) as an intentional CTA and badge accent. @@ -312,227 +313,273 @@ Sistent is the design system and UI component library for Layer5 products and op To avoid contradictory guidance across the repository, documentation responsibilities are strictly separated: -* **`DESIGN.md` (UI & Design Contract)**: - * **Owns**: Visual philosophy, semantic design tokens (frontmatter), verified token-to-code bridges, component conventions, interaction states, responsive layouts, evidence-backed accessibility guidance, and UI-specific AI guardrails. - * **Strict Non-Goals**: Build tooling, rollup/dts configurations, npm releases, package dependency categorization, multi-repo schema definitions, and git commit signing (DCO). -* **`AGENTS.md` (Repository & Engineering Contract)**: - * **Owns**: Contributor engineering runbooks, build/test gates, declaration bundling troubleshooting, optional peer rules, schema derivation contracts, and DCO requirements. - * **Strict Non-Goals**: Visual styling choices, typography variants, component color palettes, and UI layout rules. -* **`package.json`**: - * **Sole Authority** for installed framework and library versions (e.g. `@mui/material`, `react`, `typescript`). +- **`DESIGN.md` (UI & Design Contract)**: + - **Owns**: Visual philosophy, semantic design tokens (frontmatter), verified token-to-code bridges, component conventions, interaction states, responsive layouts, evidence-backed accessibility guidance, and UI-specific AI guardrails. + - **Strict Non-Goals**: Build tooling, rollup/dts configurations, npm releases, package dependency categorization, multi-repo schema definitions, and git commit signing (DCO). +- **`AGENTS.md` (Repository & Engineering Contract)**: + - **Owns**: Contributor engineering runbooks, build/test gates, declaration bundling troubleshooting, optional peer rules, schema derivation contracts, and DCO requirements. + - **Strict Non-Goals**: Visual styling choices, typography variants, component color palettes, and UI layout rules. +- **`package.json`**: + - **Sole Authority** for installed framework and library versions (e.g. `@mui/material`, `react`, `typescript`). --- -## 2. The Two-Layer Model: Design Tokens vs Runtime MUI Theme +## 2. Design System Architecture: The Five Implementation Layers -> ### Core Principle: DESIGN TOKENS ≠ MUI THEME API -> Sistent establishes an explicit two-layer design contract: -> 1. **Layer 1: Semantic Design Tokens**: Abstract design concepts defined in the YAML frontmatter (`colors.primary`, `spacing.lg`, `rounded.sm`). These define visual relationships, scales, and component compositions across design tools and specifications. -> 2. **Layer 2: Canonical Runtime Implementation**: How React and TypeScript code consumes those tokens via Material UI (`theme.palette.interactive.primary`, `theme.spacing(2)`, `theme.shape.borderRadius`). -> -> **AI Coding Rule**: Never write design tokens as literal runtime theme properties (e.g. do not write `theme.spacing.lg`, `theme.rounded.sm`, or `theme.shadows['card-accent']`). Instead, use the explicit mappings below to translate design tokens into their canonical runtime implementations. +To make `DESIGN.md` an accurate, maintainable UI and design contract for humans and AI agents, the design vocabulary is kept distinct from runtime library APIs. Sistent recognizes five explicit architectural categories: + +1. **Formal Design Tokens**: + Abstract, platform-agnostic design concepts defined in the YAML frontmatter and design specifications (e.g., `colors.primary`, `spacing.lg`, `rounded.sm`, `typography.textH3Medium`, `strokes.light-default`). These represent the design system's legitimate semantic vocabulary and visual relationships across tools and specs. + > **Integrity Rule**: Legitimate semantic design tokens must not be renamed, flattened, or erased solely because a runtime implementation library (MUI) exposes a different programmatic API. +2. **Canonical Theme Paths**: + Official, typed Material UI theme properties defined in Sistent's theme architecture (`src/theme/palette.ts`, `src/theme/typography.ts`, `src/theme/theme.ts`) designed for application-level consumer code (e.g., `theme.palette.interactive.primary`, `theme.palette.surface.primary`, `theme.palette.border.default`, `theme.shape.borderRadius`, `theme.spacing(factor)`). + > **Bridge Rule**: Runtime theme paths are documented as **implementation mappings** for formal design tokens, not as token replacements. +3. **Active Component Conventions**: + Styling patterns and internal palette bindings actively implemented inside Sistent's component modifiers (`src/theme/components/*.modifier.ts`) or custom composite components (e.g., contained buttons binding `theme.palette.background.brand?.default`, tab bars binding `theme.palette.background.tabs`, cards binding `theme.palette.background.card`). +4. **Legacy / Compatibility Paths**: + Historical theme properties maintained strictly to ensure backward compatibility with older components or downstream consumer applications like Meshery (e.g., `theme.palette.background.default` as the MUI fallback canvas, `theme.palette.primary.main`, `theme.palette.text.brand`). +5. **Observed Implementation Literals**: + Concrete pixel, rem, or color literals currently hardcoded in source component styles because no formal theme path is bound to them (e.g., `0.5rem` / `8px` dialog Paper radius, `20px` checkbox size, `rgba(0, 0, 0, 0.5)` un-focused input border). + > **Boundary Rule**: **The five architectural categories must not be conflated.** An observed implementation literal must never become a formal design token merely because it exists in source code, nor should it be promoted to a canonical design rule without evidence of shared design-system intent. --- -## 3. Explicit Design-Token-to-Runtime-Implementation Mappings +## 3. Explicit Design-Token-to-Implementation Mappings -Every documented implementation path below is verified against current repository source code or explicitly identified as a historical/compatibility reference. +Each table below establishes the explicit bridge from the **Formal Design Token** to its **Implementation Mapping**, classifying the layer and citing verified repository source evidence. + +> **Multiple Consumers of One Design Token**: +> A formal design token may have multiple distinct semantic consumers. Those consumers are related by the shared design value, but are not interchangeable runtime APIs. +> +> For example: +> +> - `colors.accent` → primitive/design value → `background.cta.default` and `catalogStatus.official` +> - `colors.secondary` → primitive/design value → `text.secondary` and `icon.secondary` +> +> An AI agent must not infer that the semantic consumer paths themselves are interchangeable. ### A. Colors & Surface Semantics > **Surface Semantics Rule**: Do not assume `theme.palette.background.card` is the universal token for all surfaces. Sistent distinguishes between application canvases, cards, modal bodies, panels, and data surfaces based on their structural role. -| Design Token | Design Value (Light / Dark) | Semantic Role | Canonical Implementation Path | Classification | New Code Guidance | -| :--- | :--- | :--- | :--- | :--- | :--- | -| `colors.primary` | `#00B39F` / `#00B39F` | Interactive Primary | `theme.palette.interactive.primary` | Canonical Semantic | **Use for all new interactive controls, icons, and active indicators.** | -| `colors.primary-hover` | `#41CCB3` / `#41CCB3` | Interactive Hover | `theme.palette.interactive.hover` | Canonical Semantic | **Use for hover states of primary interactive controls.** | -| `colors.primary-pressed`| `#93E6D1` / `#93E6D1` | Interactive Pressed | `theme.palette.interactive.pressed` | Canonical Semantic | **Use for active and pressed control states.** | -| `colors.secondary` | `#3C494F` / `#B1B9BC` | Secondary Text / Icon | `theme.palette.text.secondary` / `icon.secondary` | Canonical Semantic | **Use for captions, metadata, hints, and passive icons.** | -| `colors.accent` | `#EBC017` / `#EBC017` | CTA Accent (Saffron) | `theme.palette.background.cta?.default` | Active Component Binding | **Use for high-visibility Call-to-Action buttons.** | -| `colors.accent-hover` | `#FFEB6B` / `#FFEB6B` | CTA Hover | `theme.palette.background.cta?.hover` | Active Component Binding | **Use for hover states of CTA buttons.** | -| `colors.navigation-light / dark`| `#252E31` / `#000D12`| Navigation Bar | `theme.palette.navigation.primary` | Canonical Semantic | **Use for application shell navigation bars and side drawers.** | -| `colors.surface-light / dark-app`| `#FDFDFD` / `#000D12`| Surface Canvas (App) | `theme.palette.surface.primary` | Canonical Semantic | **Use for application screen canvases and modal body containers.** | -| `colors.surface-light / dark-card`| `#FFFFFF` / `#212121`| Card Container | `theme.palette.background.card` | Active Component Binding | Used for card surfaces and dark-mode table toolbars/headers. | -| `colors.surface-light / dark-muted`| `#F6F8F8` / `#15272F`| Panel / Data Surface | `theme.palette.background.surfaces` | Active Component Binding | Used for floating panel bodies, sidebars, and light-mode table headers. | -| `colors.surface-light / dark-tabs`| `#F6F8F8` / `#1A1A1A`| Tabs Background | `theme.palette.background.tabs` | Active Component Binding | Used for segmented tab container bars (`tab.modifier.ts`). | -| `strokes.light / dark-default`| `#EAEDEE` / `#15272F`| Border Default | `theme.palette.border.default` | Canonical Semantic | **Use for standard card outlines and container dividers.** | -| `strokes.light / dark-strong`| `#28353A` / `#8D9FA7`| Border Strong | `theme.palette.border.strong` | Canonical Semantic | **Use for emphasized borders and active boundaries.** | -| `strokes.light / dark-normal`| `#8C999E` / `#3D4F57`| Border Normal | `theme.palette.border.normal` | Canonical Semantic | **Use for intermediate divider contrast.** | -| `icons.light / dark-primary`| `#15272F` / `#D2D8DA`| Primary Icon Fill | `theme.palette.icon.default` | Canonical Semantic | **Use for standard icons.** | -| `status-colors.info` | `#2196F3` / `#2196F3` | System Info | `theme.palette.status.info` | Canonical Semantic | **Use for informative alerts and status badges.** | -| `status-colors.success`| `#206D24` / `#206D24` | System Success | `theme.palette.status.success` | Canonical Semantic | **Use for success states and confirmation badges.** | -| `status-colors.warning`| `#F0A303` / `#F0A303` | System Warning | `theme.palette.status.warning` | Canonical Semantic | **Use for warnings and caution alerts.** | -| `status-colors.error` | `#F91313` / `#F91313` | System Error | `theme.palette.status.error` | Canonical Semantic | **Use for errors, alert banners, and destructive actions.** | -| `gradients.tint-light / dark`| Gradient | Surface Header Tint | `theme.palette.surface.tint` | Canonical Semantic | **Use for modal, drawer, and panel header gradient banners.** | -| — | — | App Canvas (Legacy) | `theme.palette.background.default` | Legacy / Compatibility | Baseline MUI background; prefer `surface.primary` in new code. | -| — | — | MUI Primary Main | `theme.palette.primary.main` | Deprecated | Avoid direct theme path in new code; pass `color="primary"` to Sistent components. | -| — | — | Text Brand | `theme.palette.text.brand` | Deprecated | Avoid in new code; use `theme.palette.interactive.primary`. | +| Formal Design Token | Design Value (Light / Dark) | Semantic Role | Implementation Mapping | Implementation Layer | Consumer Guidance & Source Evidence | +| :---------------------------------- | :-------------------------- | :------------------------------ | :-------------------------------------- | :------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `colors.primary` | `#00B39F` / `#00B39F` | Interactive Primary (Primitive) | `theme.palette.interactive.primary` | Canonical Theme Path | **Primary controls & active cues** (`src/theme/palette.ts`) | +| `colors.primary-hover` | `#41CCB3` / `#41CCB3` | Interactive Hover (Primitive) | `theme.palette.interactive.hover` | Canonical Theme Path | **Hover state for primary controls** (`src/theme/palette.ts`) | +| `colors.primary-pressed` | `#93E6D1` / `#93E6D1` | Interactive Pressed (Primitive) | `theme.palette.interactive.pressed` | Canonical Theme Path | **Active & pressed states** (`src/theme/palette.ts`) | +| `colors.secondary` | `#3C494F` / `#B1B9BC` | Secondary Neutral (Primitive) | `Colors.CHARCOAL` (`#3C494F`) | Formal Design Token | Primitive neutral value (`src/theme/colors/colors.ts`); consumed via distinct semantic paths below | +| ↳ _(semantic text consumer)_ | `#3C494F` / `#B1B9BC` | Secondary Text / Captions | `theme.palette.text.secondary` | Canonical Theme Path | Secondary body copy, hints, metadata (`src/theme/palette.ts`) | +| ↳ _(semantic icon consumer)_ | `#3C494F` / `#B1B9BC` | Secondary Icon Fill | `theme.palette.icon.secondary` | Canonical Theme Path | Passive / secondary icon elements (`src/theme/palette.ts`) | +| `colors.accent` | `#EBC017` / `#EBC017` | Saffron Accent (Primitive) | `Colors.SAFFRON` (`#EBC017`) | Formal Design Token | Primitive saffron value (`src/theme/colors/colors.ts`); consumed via distinct semantic paths below | +| ↳ _(semantic CTA consumer)_ | `#EBC017` / `#EBC017` | Call-to-Action Surface | `theme.palette.background.cta?.default` | Active Component Convention | High-visibility CTA button fills (`src/theme/palette.ts`, `src/theme/components/button.modifier.ts`) | +| ↳ _(semantic catalog consumer)_ | `#EBC017` / `#EBC017` | Official Catalog Status Badge | `theme.palette.catalogStatus.official` | Canonical Theme Path | Official status ribbons and badges (`src/theme/palette.ts`) | +| `colors.accent-hover` | `#FFEB6B` / `#FFEB6B` | CTA Hover Accent (Primitive) | `#FFEB6B` | Formal Design Token | Primitive CTA hover value; consumed via distinct semantic path below | +| ↳ _(semantic CTA hover consumer)_ | `#FFEB6B` / `#FFEB6B` | Call-to-Action Hover | `theme.palette.background.cta?.hover` | Active Component Convention | Hover state for CTA buttons (`src/theme/palette.ts`) | +| `colors.navigation-light / dark` | `#252E31` / `#000D12` | Navigation Bar | `theme.palette.navigation.primary` | Canonical Theme Path | Application shell navigation bars and side drawers (`src/theme/palette.ts`) | +| `colors.surface-light / dark-app` | `#FDFDFD` / `#000D12` | Surface Canvas (App) | `theme.palette.surface.primary` | Canonical Theme Path | Application screen canvases and modal containers (`src/theme/palette.ts`) | +| `colors.surface-light / dark-card` | `#FFFFFF` / `#212121` | Card / Table Container Binding | `theme.palette.background.card` | Active Component Binding | Actively bound across Sistent card widgets and tables (`src/custom/DataTableToolbar/DataTableToolbar.tsx`, `src/custom/DashboardWidgets/RecentDesignWidget.tsx`, `src/theme/components/table.modifier.ts`). Prefer `surface.elevated` for new semantic usage as supported by theme architecture (`src/theme/theme.ts`). | +| `colors.surface-light / dark-muted` | `#F6F8F8` / `#15272F` | Panel / Data Surface | `theme.palette.background.surfaces` | Active Component Convention | Panel bodies (`src/custom/Panel/style.tsx`), table headers (`src/theme/components/table.modifier.ts`, `src/theme/palette.ts`) | +| `colors.surface-light / dark-tabs` | `#F6F8F8` / `#1A1A1A` | Tabs Background | `theme.palette.background.tabs` | Active Component Convention | Segmented tab container bars (`src/theme/components/tab.modifier.ts`, `src/theme/palette.ts`) | +| `strokes.light / dark-default` | `#EAEDEE` / `#15272F` | Border Default | `theme.palette.border.default` | Canonical Theme Path | Standard card borders and dividers (`src/theme/palette.ts`) | +| `strokes.light / dark-strong` | `#28353A` / `#8D9FA7` | Border Strong | `theme.palette.border.strong` | Canonical Theme Path | Emphasized outlines and active boundaries (`src/theme/palette.ts`) | +| `strokes.light / dark-normal` | `#8C999E` / `#3D4F57` | Border Normal | `theme.palette.border.normal` | Canonical Theme Path | Intermediate divider contrast (`src/theme/palette.ts`) | +| `icons.light / dark-primary` | `#15272F` / `#D2D8DA` | Primary Icon Fill | `theme.palette.icon.default` | Canonical Theme Path | Standard icon fill (`src/theme/palette.ts`) | +| `status-colors.info` | `#2196F3` / `#2196F3` | System Info | `theme.palette.status.info` | Canonical Theme Path | Informative alerts and chips (`src/theme/palette.ts`) | +| `status-colors.success` | `#206D24` / `#206D24` | System Success | `theme.palette.status.success` | Canonical Theme Path | Confirmation badges and success alerts (`src/theme/palette.ts`) | +| `status-colors.warning` | `#F0A303` / `#F0A303` | System Warning | `theme.palette.status.warning` | Canonical Theme Path | Caution alerts and warnings (`src/theme/palette.ts`) | +| `status-colors.error` | `#F91313` / `#F91313` | System Error | `theme.palette.status.error` | Canonical Theme Path | Error alerts and destructive actions (`src/theme/palette.ts`) | +| `gradients.tint-light / dark` | Gradient | Header Tint Gradient | `theme.palette.surface.tint` | Canonical Theme Path | Modal headers and drawer banners (`src/theme/palette.ts`) | +| — | — | App Canvas (MUI default) | `theme.palette.background.default` | Legacy / Compatibility | Baseline MUI fallback canvas (`src/theme/palette.ts`; prefer `surface.primary` in new code) | +| — | `#FFFFFF` / `#3C494F` | Elevated Surface (Semantic) | `theme.palette.surface.elevated` | Canonical Theme Path | Semantic palette definition for elevated card surfaces (`src/theme/palette.ts`, `src/theme/theme.ts`). Dark value resolves to `Colors.charcoal[40]` (`#3C494F`). Note: base `Card` (`src/base/Card/Card.tsx`) wraps MUI Card with default Paper elevation and radial wash (`src/theme/components/card.modifier.ts`) rather than setting this token directly. | +| — | — | MUI Primary Main | `theme.palette.primary.main` | Legacy / Compatibility | Baseline MUI primary (`src/theme/theme.ts`; prefer Sistent components or `interactive.primary`) | +| — | — | Brand Text | `theme.palette.text.brand` | Legacy / Compatibility | Older text brand property; prefer `theme.palette.interactive.primary` in new code (`src/theme/palette.ts`) | +| — | `rgba(0, 0, 0, 0.5)` | Un-focused Input Outline | `rgba(0, 0, 0, 0.5)` | Observed Implementation Literal | Hardcoded in `src/theme/components/outlinedinput.modifier.ts` and `src/theme/components/input.modifier.ts` | --- -### B. Spacing: Semantic Tokens vs Functional Invocation +### B. Spacing: Semantic Tokens vs Implementation Mapping + +Spacing follows an **8px base grid rhythm**. In design specifications and frontmatter contracts, spacing is expressed via semantic design tokens (`spacing.*`). At runtime, Material UI implements this grid through the functional invocation `theme.spacing(factor)`, where `factor = pixelValue / 8`. -> **AI Rule**: `spacing.*` tokens define the design grid rhythm. At runtime, the MUI theme implements this via an invocable function `theme.spacing(factor)`. Never write `theme.spacing.base` or `theme.spacing.lg` in code. +> **Mapping Rule**: `spacing.lg → 16px → theme.spacing(2)`. +> Designers and architects specify spacing using semantic tokens. Code authors and AI agents write the canonical runtime invocation `theme.spacing(factor)` rather than looking for phantom object properties like `theme.spacing.lg`. -| Design Token | Design Value | Canonical Runtime Implementation | Usage Context | -| :--- | :--- | :--- | :--- | -| `spacing.xxs` | `2px` | `theme.spacing(0.25)` or `2px` literal | Micro gaps, border adjustments | -| `spacing.xs` | `4px` | `theme.spacing(0.5)` | Micro gaps, tight chip padding | -| `spacing.sm` / `spacing.base` | `8px` | `theme.spacing(1)` | Base element separation | -| `spacing.md` | `12px` | `theme.spacing(1.5)` | Compact card gutters, internal control padding | -| `spacing.lg` | `16px` | `theme.spacing(2)` | Standard container padding, button horizontal padding | -| `spacing.xl` | `20px` | `theme.spacing(2.5)` | Modal and panel padding | -| `spacing.xxl` | `24px` | `theme.spacing(3)` | Section separation | -| `spacing.xxxl` | `32px` | `theme.spacing(4)` | Major layout gutters | +| Formal Design Token | Design Value (Spec) | Implementation Mapping | Grid Multiplier | Usage / Context | +| :---------------------------- | :------------------ | :--------------------- | :-------------- | :--------------------------------------------------------------------------------- | +| `spacing.xxs` | `2px` | `theme.spacing(0.25)` | 0.25× | Micro gaps, sub-pixel alignments (`2px` CSS literal approved strictly for borders) | +| `spacing.xs` | `4px` | `theme.spacing(0.5)` | 0.5× | Tight chip/tag padding, inner gaps | +| `spacing.sm` / `spacing.base` | `8px` | `theme.spacing(1)` | 1.0× | Base element separation (grid anchor) | +| `spacing.md` | `12px` | `theme.spacing(1.5)` | 1.5× | Compact card gutters, internal control padding | +| `spacing.lg` | `16px` | `theme.spacing(2)` | 2.0× | Standard container padding, button horizontal padding | +| `spacing.xl` | `20px` | `theme.spacing(2.5)` | 2.5× | Modal and panel padding | +| `spacing.xxl` | `24px` | `theme.spacing(3)` | 3.0× | Section separation | +| `spacing.xxxl` | `32px` | `theme.spacing(4)` | 4.0× | Major layout gutters | --- -### C. Border Radii: Semantic Tokens vs Theme Shapes & Conventions +### C. Border Radii (Shapes): Semantic Tokens vs Implementation Mapping + +Sistent defines a cohesive scale of border radii for controls and containers. In the runtime MUI theme, a single formal shape property is exposed: `theme.shape.borderRadius` (`4px`). Other radii in the design scale are implemented via component modifiers, composite wrappers, or explicit CSS literals. -> **AI Rule**: Sistent defines a single formal theme token: `theme.shape.borderRadius` (`4px`). Other radii in the `rounded.*` token scale represent component-level CSS conventions. Sistent does not define a `theme.rounded` object. +> **Mapping Rule**: `rounded.sm → 4px → theme.shape.borderRadius`. +> An observed literal (e.g. `8px` on dialog containers) is documented as an implementation literal or component convention, not as a replacement for the design token `rounded.lg`. -| Design Token | Design Value | Canonical Implementation Mechanism | Component Conventions | -| :--- | :--- | :--- | :--- | -| `rounded.xs` | `2px` | CSS literal (`2px`) | Micro controls (checkboxes, inner tags) | -| `rounded.sm` | `4px` | `theme.shape.borderRadius` | Standard controls (Buttons, TextFields, base Cards) | -| `rounded.md` | `5px` | CSS literal (`5px` / `0.5rem`) | Modal dialog wrappers, header caps | -| `rounded.lg` | `8px` | CSS literal (`8px` / `0.5rem`) | Modal dialog containers, floating panels | -| `rounded.xl` | `15px` | CSS literal (`15px`) | Featured card variants | -| `rounded.xxl` | `16px` | CSS literal (`16px`) | Catalog showcase cards | -| `rounded.full` | `9999px` | CSS literal (`9999px`) | Badges, rounded pills, avatars | +| Formal Design Token | Design Value (Spec) | Implementation Mapping | Implementation Layer | Component Context / Evidence | +| :------------------ | :------------------ | :----------------------------- | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------- | +| `rounded.xs` | `2px` | CSS literal (`2px`) | Observed Implementation Literal | Micro controls (`src/theme/components/checkbox.modifier.ts`) | +| `rounded.sm` | `4px` | `theme.shape.borderRadius` | Canonical Theme Path | Standard controls: Buttons, TextFields, base Cards (`src/theme/theme.ts`) | +| `rounded.md` | `5px` | CSS literal (`5px`) | Observed Implementation Literal | Modal card headers & wrappers (`src/custom/ModalCard/style.tsx`, `src/custom/Dialog/style.tsx`) | +| `rounded.lg` | `8px` | CSS literal (`8px` / `0.5rem`) | Observed Implementation Literal | Modal dialog Paper (`src/custom/Modal/index.tsx`), floating panels (`src/custom/Panel/style.tsx`) | +| `rounded.xl` | `15px` | CSS literal (`15px`) | Observed Implementation Literal | Featured card variants & image wells (`src/custom/CustomImage/CustomImage.tsx`) | +| `rounded.xxl` | `16px` | CSS literal (`16px`) | Active Component Convention | Catalog showcase cards (`src/custom/CustomCatalog/CustomCard.tsx`, `src/custom/DashboardWidgets/styles.tsx`) | +| `rounded.full` | `9999px` | CSS literal (`9999px` / `50%`) | Active Component Convention | Avatars (`src/custom/CollaboratorAvatarGroup/CollaboratorAvatarGroup.tsx`), pills, badges (`src/custom/Carousel/style.tsx`) | --- ### D. Typography System -All 10 custom variants are registered in `MuiTypography` via `src/theme/typography.ts`. Consumer code must use these named variants rather than declaring arbitrary font styles: - -| Design Token | Typography Spec | Canonical Runtime Usage | Responsive Behavior (`down('sm')`) | Primary Role | -| :--- | :--- | :--- | :--- | :--- | -| `typography.textH1Bold` | `3.25rem` / `4rem`, 700 | `` | Scales to `2rem` / `2.5rem` | Top-level screen headers | -| `typography.textH2Medium` | `2rem` / `2.5rem`, 500 | `` | Scales to `1.5rem` / `2.25rem` | Primary section headers | -| `typography.textH3Medium` | `1.5rem` / `2.25rem`, 500 | `` | Scales to `1rem` / `1.75rem` (700) | Card & modal titles | -| `typography.textB1Regular` | `1rem` / `1.75rem`, 400 | `` | Preserves `1rem` / `1.75rem` | Primary UI & body copy | -| `typography.textB2SemiBold`| `1rem` / `1.75rem`, 600, `capitalize` | `` | Preserves `1rem` / `1.75rem` | Action labels, active tab titles | -| `typography.textB3Regular` | `0.875rem` / `1.5rem`, 400 | `` | Fixed | Supporting labels, hints | -| `typography.textL1Bold` | `0.75rem` / `1rem`, 700 | `` | Fixed | Ribbon badges, small chips | -| `typography.textL2Regular` | `0.75rem` / `1.5rem`, 400 | `` | Fixed | Secondary metadata, captions | -| `typography.textC1Regular` | `0.75rem` / `1.5rem`, 400 | `` | Fixed | Compact code blocks, UUIDs | -| `typography.textC2Regular` | `1rem` / `1.75rem`, 400 | `` | Fixed | Standard monospace text | +All 10 custom typography variants are registered in `MuiTypography` via `src/theme/typography.ts`. Consumer code must use these named variants rather than declaring arbitrary font styles: + +| Formal Design Token | Typography Spec | Canonical Runtime Usage | Responsive Behavior (`down('sm')`) | Primary Role | +| :-------------------------- | :------------------------------------ | :-------------------------------------- | :--------------------------------- | :------------------------------- | +| `typography.textH1Bold` | `3.25rem` / `4rem`, 700 | `` | Scales to `2rem` / `2.5rem` | Top-level screen headers | +| `typography.textH2Medium` | `2rem` / `2.5rem`, 500 | `` | Scales to `1.5rem` / `2.25rem` | Primary section headers | +| `typography.textH3Medium` | `1.5rem` / `2.25rem`, 500 | `` | Scales to `1rem` / `1.75rem` (700) | Card & modal titles | +| `typography.textB1Regular` | `1rem` / `1.75rem`, 400 | `` | Preserves `1rem` / `1.75rem` | Primary UI & body copy | +| `typography.textB2SemiBold` | `1rem` / `1.75rem`, 600, `capitalize` | `` | Preserves `1rem` / `1.75rem` | Action labels, active tab titles | +| `typography.textB3Regular` | `0.875rem` / `1.5rem`, 400 | `` | Fixed | Supporting labels, hints | +| `typography.textL1Bold` | `0.75rem` / `1rem`, 700 | `` | Fixed | Ribbon badges, small chips | +| `typography.textL2Regular` | `0.75rem` / `1.5rem`, 400 | `` | Fixed | Secondary metadata, captions | +| `typography.textC1Regular` | `0.75rem` / `1.5rem`, 400 | `` | Fixed | Compact code blocks, UUIDs | +| `typography.textC2Regular` | `1rem` / `1.75rem`, 400 | `` | Fixed | Standard monospace text | --- ### E. Shadows, Elevation & Depth -* **Tooltip Elevation**: Encapsulated in `src/theme/components/tooltip.modifier.ts`. Automatically renders neutral shadow in light mode and a dual-tone teal luminescent glow in dark mode. -* **Card Surface Depth**: Encapsulated via the subtle radial wash in `src/theme/components/card.modifier.ts`. -* **Floating Panels**: Composed using backdrop blur: `boxShadow: 0 4px 16px ${theme.palette.background.blur?.light}` (`src/custom/Panel/style.tsx`). -* **Showcase Cards**: Accent shadow: `boxShadow: 2px 2px 3px 0px ${theme.palette.background.brand?.default}` (`src/custom/CatalogCard/style.tsx`). -* **Negative Constraint**: `theme.shadows` is a standard MUI 25-element tuple. Named keys like `theme.shadows['card-accent']` or `theme.elevation` do not exist. +- **Tooltip Elevation**: Encapsulated in `src/theme/components/tooltip.modifier.ts`. Automatically renders neutral shadow in light mode and a dual-tone teal luminescent glow in dark mode. +- **Card Surface Depth**: Encapsulated via the subtle radial wash in `src/theme/components/card.modifier.ts`. +- **Floating Panels**: Composed using backdrop blur: `boxShadow: 0 4px 16px ${theme.palette.background.blur?.light}` (`src/custom/Panel/style.tsx`). +- **Showcase Cards**: Accent shadow: `boxShadow: 2px 2px 3px 0px ${theme.palette.background.brand?.default}` (`src/custom/CatalogCard/style.tsx`). +- **Negative Constraint**: `theme.shadows` is a standard MUI 25-element tuple. Named keys like `theme.shadows['card-accent']` or `theme.elevation` do not exist. --- ## 4. Component Usage & Import Conventions ### Import Precedence Rule + > **"Prefer `@sistent/sistent` when a Sistent abstraction exists. Use `@mui/material` when no equivalent exists or when implementing Sistent itself."** -* **Base Controls**: Always import from `@sistent/sistent`: - `Button`, `Card`, `Checkbox`, `Dialog`, `IconButton`, `Select`, `Tab`, `Tabs`, `TextField`, `Tooltip`, `Typography`. -* **Custom Components**: Always import from `@sistent/sistent`: - `ActionButton`, `CustomTooltip`, `Modal`, `Panel`, `ResponsiveDataTable`, `SearchBar`, `UniversalFilter`. -* **MUI Fallbacks**: Permitted only when building internal Sistent primitives or when no Sistent wrapper exists. +- **Base Controls**: Always import from `@sistent/sistent`: + `Button`, `Card`, `CardContent`, `CardHeader`, `CardMedia`, `Checkbox`, `Dialog`, `IconButton`, `Select`, `Tab`, `Tabs`, `TextField`, `Tooltip`, `Typography`. +- **Custom Components**: Always import from `@sistent/sistent`: + `ActionButton`, `CustomTooltip`, `DangerConfirmationModal`, `Modal`, `ModalBody`, `ModalFooter`, `Panel`, `ResponsiveDataTable`, `SearchBar`, `UniversalFilter`. +- **MUI Fallbacks**: Permitted only when building internal Sistent primitives or when no Sistent wrapper exists. --- ## 5. Interaction States Matrix -Interaction states are derived from active Sistent component implementations and theme modifiers (`button.modifier.ts`, `tab.modifier.ts`, `input.modifier.ts`): +Every documented interaction rule is grounded in source evidence and follows the verification chain: +$$\text{Source Evidence} \longrightarrow \text{Observed Behavior} \longrightarrow \text{Classification} \longrightarrow \text{Documented Rule}$$ + +Component-specific behaviors are never promoted to Canonical Design Rules without evidence of intentional, shared design-system usage. -| Interaction State | Primary Contained Control | Outlined Control | Surface / Container | Rule Status | -| :--- | :--- | :--- | :--- | :--- | -| **Default** | `theme.palette.interactive.primary` | Transparent background, `border.default` | Canonical surface token (`surface.primary`, `background.card`) | Canonical Design Rule | -| **Hover** | `theme.palette.interactive.hover` (`#41CCB3`) | `theme.palette.background.hover` | `theme.palette.background.hover` | Canonical Design Rule | -| **Pressed** | `theme.palette.interactive.pressed` (`#93E6D1`) | `theme.palette.interactive.pressed` tint | Stepped surface depth | Canonical Design Rule | -| **Focus-Visible** | Distinct focus outline: `theme.palette.border.brand` | Outline: `theme.palette.border.brand` | Outline: `theme.palette.border.brand` | Canonical Design Rule | -| **Disabled** | `theme.palette.interactive.disabled`, text `disabled` | Stroke `theme.palette.text.disabled` | N/A | Canonical Design Rule | -| **Loading** | Spinner / skeleton indicator; interactions inert | Inherits disabled state modifiers | Loading overlay / skeleton | Implementation-Specific | -| **Selected / Active** | Active tab indicator / contained toggle | `border.brand` with subtle background tint | Component-specific active fill | Implementation-Specific | -| **Error** | Background: `theme.palette.background.error?.default`, text via `getContrastText()` | Border: `theme.palette.status.error` | Border: `theme.palette.status.error` | Canonical Design Rule | -| **Success / Warning** | Component-specific status badge / icon | Border / text using status token | Background / border using status token | Implementation-Specific | +| State / Trigger | Source Evidence | Observed Behavior | Classification | Documented Rule | +| :------------------------------------------------------ | :----------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------- | +| **Contained Button Default** | `src/theme/components/button.modifier.ts` | Sets `backgroundColor: brand?.default` (`#00B39F`), `color: constant?.white` | Active Component Convention | Primary contained buttons fill with brand teal and white text. | +| **Contained Button Hover** | `src/theme/components/button.modifier.ts` | Sets `backgroundColor: brand?.hover` (`#41CCB3`) on `&:hover` | Active Component Convention | Contained buttons transition to brand hover fill (`#41CCB3`) on mouseover. | +| **Interactive Pressed / Active** | `src/theme/palette.ts` (`interactive.pressed`) | Resolves to `Colors.keppel[60]` (`#93E6D1`); button modifiers rely on standard MUI ripple rather than an active pseudo-class | Canonical Theme Path | Available in theme palette as the canonical pressed token; button components rely on native MUI ripple for click feedback. | +| **Outlined Button Default** | `src/theme/components/button.modifier.ts` | Sets `border: 1px solid ${neutral?.default}`, background transparent | Active Component Convention | Outlined buttons use neutral border with no fill. | +| **Outlined Button Hover** | `src/theme/components/button.modifier.ts` | Sets `backgroundColor: hover`, `color: TextNeutral?.default` | Active Component Convention | Outlined buttons receive subtle neutral hover tint on mouseover. | +| **Input Focus** | `src/theme/components/outlinedinput.modifier.ts` | Sets `.MuiOutlinedInput-notchedOutline` border to `brand?.default` (`#00B39F`) | Active Component Convention | Text fields highlight the active border with brand teal (`#00B39F`). | +| **Input Un-focused** | `src/theme/components/outlinedinput.modifier.ts` | Sets `borderColor: 'rgba(0, 0, 0, 0.5)'` | Observed Implementation Literal | Standard input outlines use semi-transparent neutral border when idle. | +| **Contained Button Disabled** | `src/theme/components/button.modifier.ts` | Sets `backgroundColor: brand?.disabled`, `color: disabled` | Active Component Convention | Disabled contained buttons mute to brand-disabled background and disabled text. | +| **Selected Tab** | `src/theme/components/tab.modifier.ts` | Sets `backgroundColor: background.tabs`, `color: defaultText` | Active Component Convention | Selected tabs render dedicated tab background rather than pill fills. | +| **Semantic Color Actions (Error/Success/Warning/Info)** | `src/theme/components/button.modifier.ts` | Respects semantic `color` prop, applying `error?.default`, etc., and computing contrast text with `getContrastText()` | Active Component Convention | Contained buttons with semantic color props apply status fills and accessible contrast labels. | --- ## 6. Authorization & Permissions ### Permission Rule + > **"When an action is authorization-controlled, use the existing Sistent permission mechanism and provide the appropriate `permissionKey`. Do not invent local permission logic."** -* Built-in native support exists on: `Button`, `IconButton`, `MenuItem`, `ListItem`, and `ListItemButton`. -* If unauthorized, the component automatically disables itself and shows a badge tooltip (`permissionAction="showShield"`, default) or renders nothing (`permissionAction="hide"`). -* **Applicability Scope**: Actions that are not authorization-controlled and standard informational triggers do **not** require `permissionKey`. The optional `permissionKey` prop is strictly for protected operations. -* Arbitrary custom triggers must be wrapped in ``. +- Built-in native support exists on: `Button`, `IconButton`, `MenuItem`, `ListItem`, and `ListItemButton`. +- If unauthorized, the component automatically disables itself and shows a badge tooltip (`permissionAction="showShield"`, default) or renders nothing (`permissionAction="hide"`). +- **Applicability Scope**: Actions that are not authorization-controlled and standard informational triggers do **not** require `permissionKey`. The optional `permissionKey` prop is strictly for protected operations. +- Arbitrary custom triggers must be wrapped in ``. --- ## 7. Responsive Behavior & Accessibility (a11y) Guidance -* **Breakpoints**: Standard MUI breakpoints (`xs: 0`, `sm: 600px`, `md: 900px`, `lg: 1200px`, `xl: 1536px`). -* **Header Auto-Scaling**: `textH1Bold`, `textH2Medium`, and `textH3Medium` automatically scale down below the `sm` breakpoint via `src/theme/typography.ts`. -* **Dynamic Contrast & Readability**: - * **Mechanism**: Use `readableTextColor(bg)` from `src/theme/theme.ts` when placing text over dynamic brand/custom background fills to automatically select high-contrast ink (`charcoal[10]` vs `charcoal[100]`). - * **Design Guidance**: Aim for 4.5:1 contrast on standard text and 3:1 on large text/icons against their immediate surface. - * **Enforcement Status**: Contrast is guided by theme tokens and helper utilities (`readableTextColor`), but is not enforced by a global automated linting/test suite across every component. -* **Label Capitalization**: Action labels use `textTransform: 'capitalize'` built into `textB2SemiBold`. -* **Semantic ARIA**: Icon-only buttons must supply descriptive `aria-label` and `Tooltip`. +- **Breakpoints**: Standard MUI breakpoints (`xs: 0`, `sm: 600px`, `md: 900px`, `lg: 1200px`, `xl: 1536px`). +- **Header Auto-Scaling**: `textH1Bold`, `textH2Medium`, and `textH3Medium` automatically scale down below the `sm` breakpoint via `src/theme/typography.ts`. +- **Dynamic Contrast & Readability**: + - **Mechanism**: Use `readableTextColor(bg)` from `src/theme/theme.ts` when placing text over dynamic brand/custom background fills to automatically select high-contrast ink (`charcoal[10]` vs `charcoal[100]`). + - **Design Guidance**: Aim for 4.5:1 contrast on standard text and 3:1 on large text/icons against their immediate surface. + - **Enforcement Status**: Contrast is guided by theme tokens and helper utilities (`readableTextColor`), but is not enforced by a global automated linting/test suite across every component. +- **Label Capitalization**: Action labels use `textTransform: 'capitalize'` built into `textB2SemiBold`. +- **Semantic ARIA**: Icon-only buttons must supply descriptive `aria-label` and `Tooltip`. --- ## 8. Canonical Code Recipes -All code recipes below adhere strictly to Sistent component conventions, canonical semantic tokens, accessibility guidelines, and AI guardrails: +All code recipes below are verified against Sistent's public component exports (`@sistent/sistent`), actual source props, and canonical token mappings: + +### Recipe 1: Container Card Surface with Light/Dark Support + +_Verified export_: `Card`, `CardContent`, `Typography`, `Box` from `@sistent/sistent` (`src/base/Card/Card.tsx`, `src/base/CardContent/CardContent.tsx`). + +The exported Sistent `Card` abstraction encapsulates container surface styling, default border radius (`theme.shape.borderRadius` / 4px), and the subtle radial wash from `src/theme/components/card.modifier.ts`. Similarly, `CardContent` provides default container padding. AI agents and application developers should use the clean Sistent abstraction directly rather than manually restyling properties the component already owns. -### Recipe 1: Container Surface with Light/Dark Support ```tsx import React from 'react'; -import { styled, Box, Typography } from '@sistent/sistent'; - -const CardContainer = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.card, - color: theme.palette.text.default, - border: `1px solid ${theme.palette.border.default}`, - borderRadius: theme.shape.borderRadius, - padding: theme.spacing(2), -})); - -export const CustomCard: React.FC<{ title: string; children: React.ReactNode }> = ({ title, children }) => ( - - - {title} - - theme.spacing(1.5) }}> - {children} - - +import { Card, CardContent, Typography, Box } from '@sistent/sistent'; + +export const ContentCard: React.FC<{ title: string; children: React.ReactNode }> = ({ + title, + children +}) => ( + + + + {title} + + {children} + + ); ``` +> **Deliberate Customization Note**: When building a specialized variant (such as an outlined card with a visible stroke or custom padding), apply explicit styling via standard MUI `sx` object notation (e.g. ``). Do not manually recreate base card styles when the standard Sistent abstraction suffices. + ### Recipe 2: Authorization-Controlled Action Button -> **Note**: This pattern applies strictly to actions that are actually authorization-controlled. The optional `permissionKey` must not be interpreted as a requirement for every button or action in the system; standard actions and informational triggers should omit `permissionKey`. +_Verified export_: `Button` from `@sistent/sistent` (`src/base/Button/Button.tsx`). + +> **Note**: This pattern applies strictly to actions that are authorization-controlled and require an explicit permission gate. Standard actions and informational triggers should omit `permissionKey`. ```tsx import React from 'react'; import { Button } from '@sistent/sistent'; import type { Key } from '@meshery/schemas/permissions'; -interface ActionProps { +interface ProtectedActionProps { label: string; onClick: () => void; - permissionKey?: Key; + permissionKey: Key; } -export const ActionButton: React.FC = ({ label, onClick, permissionKey }) => ( +export const ProtectedActionButton: React.FC = ({ + label, + onClick, + permissionKey +}) => ( + + + ); ``` +> **Modal Accessibility Note (`aria-labelledby`)**: Sistent's `Modal` component internally hardcodes `aria-labelledby="alert-dialog-slide-title"` and `aria-describedby="alert-dialog-slide-description"` on the underlying dialog, which overrides any caller-provided `aria-label`. To ensure the dialog has a valid accessible name and description in the accessibility tree without duplicating visible text, callers must supply matching element IDs using a visually-hidden style (`sx={visuallyHidden}` from `@mui/utils` or standard visually-hidden CSS properties) within the modal body. + --- ## 9. AI Coding Guardrails (UI & Styling Specific) 1. **Design Tokens ≠ Runtime Theme API**: - > Translate semantic tokens into canonical runtime calls (`theme.spacing(2)`, `theme.shape.borderRadius`). Never write `theme.spacing.lg` or `theme.rounded.sm`. + > Translate semantic tokens into canonical runtime calls (`spacing.lg` → `theme.spacing(2)`, `rounded.sm` → `theme.shape.borderRadius`). Never write phantom paths like `theme.spacing.lg` or `theme.rounded.sm`. 2. **Color Literal Rule**: > Consumer/UI code must not introduce new raw color literals when an existing semantic Sistent token applies. Theme source and token definitions are exempt. 3. **Spacing & Shape Token Rule**: - > Consumer/UI code must use `theme.spacing(n)` for element layout and gutters. Do not invent arbitrary inline pixel offsets when an 8px grid factor satisfies the requirement. + > Consumer/UI code must use `theme.spacing(factor)` for element layout and gutters. Do not invent arbitrary inline pixel offsets when an 8px grid factor satisfies the requirement. 4. **Component Import Rule**: > Prefer `@sistent/sistent` when a Sistent abstraction exists. Use `@mui/material` when no equivalent exists or when implementing Sistent itself. 5. **Permission Rule**: > When an action is authorization-controlled, use the existing Sistent permission mechanism and provide the appropriate `permissionKey`. Do not invent local permission logic. 6. **No Manual Dark-Mode Forking**: - > Avoid manual conditionals like `theme.palette.mode === 'dark' ? '#212121' : '#fff'`. Use semantic tokens (`theme.palette.background.card`, `theme.palette.surface.primary`, `theme.palette.text.default`) that resolve automatically. + > Avoid manual conditionals like `theme.palette.mode === 'dark' ? '#212121' : '#fff'`. Use semantic tokens that resolve automatically (`theme.palette.background.card` when maintaining existing component bindings, `theme.palette.surface.elevated` for new semantic card surfaces, `theme.palette.surface.primary`, `theme.palette.text.default`). 7. **Preserve Typography Variants**: > Use `` through `` rather than ad-hoc inline font declarations. @@ -605,12 +668,13 @@ export const SistentModal: React.FC<{ ## 10. Documentation / Implementation Drift Protocol When `DESIGN.md` and repository source code disagree: + 1. **Do not silently change the design contract.** 2. **Determine discrepancy classification**: - * **Documentation Drift**: The source implementation was updated intentionally, but `DESIGN.md` was not updated to match (e.g. `surface-dark-card` frontmatter was `#121212` vs code `#212121`). - * **Implementation Drift**: A component diverges from established design tokens due to an accidental or ad-hoc local override. - * **Intentional Legacy / Compatibility Behavior**: An older token path maintained to avoid breaking consumer downstream imports (e.g. `background.default`). - * **Intentional Exception**: A component has a specialized visual requirement that intentionally diverges from the standard token. + - **Documentation Drift**: The source implementation was updated intentionally, but `DESIGN.md` was not updated to match (e.g. `surface-dark-card` frontmatter was `#121212` vs code `#212121`). + - **Implementation Drift**: A component diverges from established design tokens due to an accidental or ad-hoc local override. + - **Intentional Legacy / Compatibility Behavior**: An older token path maintained to avoid breaking consumer downstream imports (e.g. `background.default`). + - **Intentional Exception**: A component has a specialized visual requirement that intentionally diverges from the standard token. 3. **Record the discrepancy in the audit log.** 4. **Only change the design contract when there is an explicit design-system decision to do so.** 5. **Do not make unrelated design changes during AI-context work.** @@ -619,12 +683,12 @@ When `DESIGN.md` and repository source code disagree: ## 11. Do's and Don'ts -* **Do** keep teal (`#00B39F`) as the default primary interaction color. -* **Do** reserve gradients (`surface.tint`) for modal headers, drawer banners, and structural framing. -* **Do** prefer tonal layering, border contrast, and subtle surface depth before adding large shadows. -* **Do** treat dark mode as a fully supported surface system, not a simple color inversion. -* **Do** use `theme.spacing(factor)` for all layout margins and paddings. -* **Don't** flood content areas with saturated fills. -* **Don't** overuse large radii on utilitarian controls; reserve `16px` for showcase cards. -* **Don't** replace neutral text with accent colors unless the content is genuinely interactive or status-bearing. -* **Don't** write phantom theme paths like `theme.spacing.lg` or `theme.rounded.sm`. +- **Do** keep teal (`#00B39F`) as the default primary interaction color. +- **Do** reserve gradients (`surface.tint`) for modal headers, drawer banners, and structural framing. +- **Do** prefer tonal layering, border contrast, and subtle surface depth before adding large shadows. +- **Do** treat dark mode as a fully supported surface system, not a simple color inversion. +- **Do** use `theme.spacing(factor)` for all layout margins and paddings (`spacing.lg` → `theme.spacing(2)`). +- **Don't** flood content areas with saturated fills. +- **Don't** overuse large radii on utilitarian controls; reserve `16px` for showcase cards. +- **Don't** replace neutral text with accent colors unless the content is genuinely interactive or status-bearing. +- **Don't** write phantom theme paths like `theme.spacing.lg` or `theme.rounded.sm`. From f6aacb9d254d98c5eb8a100ef2cb2f8cf90f1315 Mon Sep 17 00:00:00 2001 From: Parth Gartan Date: Sun, 13 Sep 2026 17:02:09 +0000 Subject: [PATCH 3/4] docs: address contributor review on design contract tokens and recipes Signed-off-by: Parth Gartan --- DESIGN.md | 128 +++++++++++++++++------------------------------------- 1 file changed, 40 insertions(+), 88 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 16854f9e2..eb814b9ee 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -19,7 +19,7 @@ colors: navigation-dark: '#000D12' on-navigation-dark: '#FDFDFD' surface-light-app: '#FDFDFD' - surface-light-card: '#FFFFFF' + surface-light-card: '#FDFDFD' surface-light-muted: '#F6F8F8' surface-light-tabs: '#F6F8F8' on-surface-light: '#000D12' @@ -362,38 +362,38 @@ Each table below establishes the explicit bridge from the **Formal Design Token* > **Surface Semantics Rule**: Do not assume `theme.palette.background.card` is the universal token for all surfaces. Sistent distinguishes between application canvases, cards, modal bodies, panels, and data surfaces based on their structural role. -| Formal Design Token | Design Value (Light / Dark) | Semantic Role | Implementation Mapping | Implementation Layer | Consumer Guidance & Source Evidence | -| :---------------------------------- | :-------------------------- | :------------------------------ | :-------------------------------------- | :------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `colors.primary` | `#00B39F` / `#00B39F` | Interactive Primary (Primitive) | `theme.palette.interactive.primary` | Canonical Theme Path | **Primary controls & active cues** (`src/theme/palette.ts`) | -| `colors.primary-hover` | `#41CCB3` / `#41CCB3` | Interactive Hover (Primitive) | `theme.palette.interactive.hover` | Canonical Theme Path | **Hover state for primary controls** (`src/theme/palette.ts`) | -| `colors.primary-pressed` | `#93E6D1` / `#93E6D1` | Interactive Pressed (Primitive) | `theme.palette.interactive.pressed` | Canonical Theme Path | **Active & pressed states** (`src/theme/palette.ts`) | -| `colors.secondary` | `#3C494F` / `#B1B9BC` | Secondary Neutral (Primitive) | `Colors.CHARCOAL` (`#3C494F`) | Formal Design Token | Primitive neutral value (`src/theme/colors/colors.ts`); consumed via distinct semantic paths below | -| ↳ _(semantic text consumer)_ | `#3C494F` / `#B1B9BC` | Secondary Text / Captions | `theme.palette.text.secondary` | Canonical Theme Path | Secondary body copy, hints, metadata (`src/theme/palette.ts`) | -| ↳ _(semantic icon consumer)_ | `#3C494F` / `#B1B9BC` | Secondary Icon Fill | `theme.palette.icon.secondary` | Canonical Theme Path | Passive / secondary icon elements (`src/theme/palette.ts`) | -| `colors.accent` | `#EBC017` / `#EBC017` | Saffron Accent (Primitive) | `Colors.SAFFRON` (`#EBC017`) | Formal Design Token | Primitive saffron value (`src/theme/colors/colors.ts`); consumed via distinct semantic paths below | -| ↳ _(semantic CTA consumer)_ | `#EBC017` / `#EBC017` | Call-to-Action Surface | `theme.palette.background.cta?.default` | Active Component Convention | High-visibility CTA button fills (`src/theme/palette.ts`, `src/theme/components/button.modifier.ts`) | -| ↳ _(semantic catalog consumer)_ | `#EBC017` / `#EBC017` | Official Catalog Status Badge | `theme.palette.catalogStatus.official` | Canonical Theme Path | Official status ribbons and badges (`src/theme/palette.ts`) | -| `colors.accent-hover` | `#FFEB6B` / `#FFEB6B` | CTA Hover Accent (Primitive) | `#FFEB6B` | Formal Design Token | Primitive CTA hover value; consumed via distinct semantic path below | -| ↳ _(semantic CTA hover consumer)_ | `#FFEB6B` / `#FFEB6B` | Call-to-Action Hover | `theme.palette.background.cta?.hover` | Active Component Convention | Hover state for CTA buttons (`src/theme/palette.ts`) | -| `colors.navigation-light / dark` | `#252E31` / `#000D12` | Navigation Bar | `theme.palette.navigation.primary` | Canonical Theme Path | Application shell navigation bars and side drawers (`src/theme/palette.ts`) | -| `colors.surface-light / dark-app` | `#FDFDFD` / `#000D12` | Surface Canvas (App) | `theme.palette.surface.primary` | Canonical Theme Path | Application screen canvases and modal containers (`src/theme/palette.ts`) | -| `colors.surface-light / dark-card` | `#FFFFFF` / `#212121` | Card / Table Container Binding | `theme.palette.background.card` | Active Component Binding | Actively bound across Sistent card widgets and tables (`src/custom/DataTableToolbar/DataTableToolbar.tsx`, `src/custom/DashboardWidgets/RecentDesignWidget.tsx`, `src/theme/components/table.modifier.ts`). Prefer `surface.elevated` for new semantic usage as supported by theme architecture (`src/theme/theme.ts`). | -| `colors.surface-light / dark-muted` | `#F6F8F8` / `#15272F` | Panel / Data Surface | `theme.palette.background.surfaces` | Active Component Convention | Panel bodies (`src/custom/Panel/style.tsx`), table headers (`src/theme/components/table.modifier.ts`, `src/theme/palette.ts`) | -| `colors.surface-light / dark-tabs` | `#F6F8F8` / `#1A1A1A` | Tabs Background | `theme.palette.background.tabs` | Active Component Convention | Segmented tab container bars (`src/theme/components/tab.modifier.ts`, `src/theme/palette.ts`) | -| `strokes.light / dark-default` | `#EAEDEE` / `#15272F` | Border Default | `theme.palette.border.default` | Canonical Theme Path | Standard card borders and dividers (`src/theme/palette.ts`) | -| `strokes.light / dark-strong` | `#28353A` / `#8D9FA7` | Border Strong | `theme.palette.border.strong` | Canonical Theme Path | Emphasized outlines and active boundaries (`src/theme/palette.ts`) | -| `strokes.light / dark-normal` | `#8C999E` / `#3D4F57` | Border Normal | `theme.palette.border.normal` | Canonical Theme Path | Intermediate divider contrast (`src/theme/palette.ts`) | -| `icons.light / dark-primary` | `#15272F` / `#D2D8DA` | Primary Icon Fill | `theme.palette.icon.default` | Canonical Theme Path | Standard icon fill (`src/theme/palette.ts`) | -| `status-colors.info` | `#2196F3` / `#2196F3` | System Info | `theme.palette.status.info` | Canonical Theme Path | Informative alerts and chips (`src/theme/palette.ts`) | -| `status-colors.success` | `#206D24` / `#206D24` | System Success | `theme.palette.status.success` | Canonical Theme Path | Confirmation badges and success alerts (`src/theme/palette.ts`) | -| `status-colors.warning` | `#F0A303` / `#F0A303` | System Warning | `theme.palette.status.warning` | Canonical Theme Path | Caution alerts and warnings (`src/theme/palette.ts`) | -| `status-colors.error` | `#F91313` / `#F91313` | System Error | `theme.palette.status.error` | Canonical Theme Path | Error alerts and destructive actions (`src/theme/palette.ts`) | -| `gradients.tint-light / dark` | Gradient | Header Tint Gradient | `theme.palette.surface.tint` | Canonical Theme Path | Modal headers and drawer banners (`src/theme/palette.ts`) | -| — | — | App Canvas (MUI default) | `theme.palette.background.default` | Legacy / Compatibility | Baseline MUI fallback canvas (`src/theme/palette.ts`; prefer `surface.primary` in new code) | -| — | `#FFFFFF` / `#3C494F` | Elevated Surface (Semantic) | `theme.palette.surface.elevated` | Canonical Theme Path | Semantic palette definition for elevated card surfaces (`src/theme/palette.ts`, `src/theme/theme.ts`). Dark value resolves to `Colors.charcoal[40]` (`#3C494F`). Note: base `Card` (`src/base/Card/Card.tsx`) wraps MUI Card with default Paper elevation and radial wash (`src/theme/components/card.modifier.ts`) rather than setting this token directly. | -| — | — | MUI Primary Main | `theme.palette.primary.main` | Legacy / Compatibility | Baseline MUI primary (`src/theme/theme.ts`; prefer Sistent components or `interactive.primary`) | -| — | — | Brand Text | `theme.palette.text.brand` | Legacy / Compatibility | Older text brand property; prefer `theme.palette.interactive.primary` in new code (`src/theme/palette.ts`) | -| — | `rgba(0, 0, 0, 0.5)` | Un-focused Input Outline | `rgba(0, 0, 0, 0.5)` | Observed Implementation Literal | Hardcoded in `src/theme/components/outlinedinput.modifier.ts` and `src/theme/components/input.modifier.ts` | +| Formal Design Token | Design Value (Light / Dark) | Semantic Role | Implementation Mapping | Implementation Layer | Consumer Guidance & Source Evidence | +| :---------------------------------- | :-------------------------- | :------------------------------ | :-------------------------------------- | :------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `colors.primary` | `#00B39F` / `#00B39F` | Interactive Primary (Primitive) | `theme.palette.interactive.primary` | Canonical Theme Path | **Primary controls & active cues** (`src/theme/palette.ts`) | +| `colors.primary-hover` | `#41CCB3` / `#41CCB3` | Interactive Hover (Primitive) | `theme.palette.interactive.hover` | Canonical Theme Path | **Hover state for primary controls** (`src/theme/palette.ts`) | +| `colors.primary-pressed` | `#93E6D1` / `#93E6D1` | Interactive Pressed (Primitive) | `theme.palette.interactive.pressed` | Canonical Theme Path | **Active & pressed states** (`src/theme/palette.ts`) | +| `colors.secondary` | `#3C494F` / `#B1B9BC` | Secondary Neutral (Primitive) | `Colors.CHARCOAL` (`#3C494F`) | Formal Design Token | Primitive neutral value (`src/theme/colors/colors.ts`); consumed via distinct semantic paths below | +| ↳ _(semantic text consumer)_ | `#3C494F` / `#B1B9BC` | Secondary Text / Captions | `theme.palette.text.secondary` | Canonical Theme Path | Secondary body copy, hints, metadata (`src/theme/palette.ts`) | +| ↳ _(semantic icon consumer)_ | `#3C494F` / `#B1B9BC` | Secondary Icon Fill | `theme.palette.icon.secondary` | Canonical Theme Path | Passive / secondary icon elements (`src/theme/palette.ts`) | +| `colors.accent` | `#EBC017` / `#EBC017` | Saffron Accent (Primitive) | `Colors.SAFFRON` (`#EBC017`) | Formal Design Token | Primitive saffron value (`src/theme/colors/colors.ts`); consumed via distinct semantic paths below | +| ↳ _(semantic CTA consumer)_ | `#EBC017` / `#EBC017` | Call-to-Action Surface | `theme.palette.background.cta?.default` | Active Component Convention | High-visibility CTA button fills (`src/theme/palette.ts`) | +| ↳ _(semantic catalog consumer)_ | `#EBC017` / `#EBC017` | Official Catalog Status Badge | `theme.palette.catalogStatus.official` | Canonical Theme Path | Official status ribbons and badges (`src/theme/palette.ts`) | +| `colors.accent-hover` | `#FFEB6B` / `#FFEB6B` | CTA Hover Accent (Primitive) | `#FFEB6B` | Formal Design Token | Primitive CTA hover value; consumed via distinct semantic path below | +| ↳ _(semantic CTA hover consumer)_ | `#FFEB6B` / `#FFEB6B` | Call-to-Action Hover | `theme.palette.background.cta?.hover` | Active Component Convention | Hover state for CTA buttons (`src/theme/palette.ts`) | +| `colors.navigation-light / dark` | `#252E31` / `#000D12` | Navigation Bar | `theme.palette.navigation.primary` | Canonical Theme Path | Application shell navigation bars and side drawers (`src/theme/palette.ts`) | +| `colors.surface-light / dark-app` | `#FDFDFD` / `#000D12` | Surface Canvas (App) | `theme.palette.surface.primary` | Canonical Theme Path | Application screen canvases and modal containers (`src/theme/palette.ts`) | +| `colors.surface-light / dark-card` | `#FDFDFD` / `#212121` | Card / Table Container Binding | `theme.palette.background.card` | Active Component Binding | Canonical token binding for card widgets, tables, and container surfaces (`src/custom/DataTableToolbar/DataTableToolbar.tsx`, `src/custom/DashboardWidgets/RecentDesignWidget.tsx`, `src/theme/components/table.modifier.ts`). Card backgrounds map strictly to `theme.palette.background.card` to maintain visual consistency across light and dark modes. | +| `colors.surface-light / dark-muted` | `#F6F8F8` / `#15272F` | Panel / Data Surface | `theme.palette.background.surfaces` | Active Component Convention | Panel bodies (`src/custom/Panel/style.tsx`), table headers (`src/theme/components/table.modifier.ts`, `src/theme/palette.ts`) | +| `colors.surface-light / dark-tabs` | `#F6F8F8` / `#1A1A1A` | Tabs Background | `theme.palette.background.tabs` | Active Component Convention | Segmented tab container bars (`src/theme/components/tab.modifier.ts`, `src/theme/palette.ts`) | +| `strokes.light / dark-default` | `#EAEDEE` / `#15272F` | Border Default | `theme.palette.border.default` | Canonical Theme Path | Standard card borders and dividers (`src/theme/palette.ts`) | +| `strokes.light / dark-strong` | `#28353A` / `#8D9FA7` | Border Strong | `theme.palette.border.strong` | Canonical Theme Path | Emphasized outlines and active boundaries (`src/theme/palette.ts`) | +| `strokes.light / dark-normal` | `#8C999E` / `#3D4F57` | Border Normal | `theme.palette.border.normal` | Canonical Theme Path | Intermediate divider contrast (`src/theme/palette.ts`) | +| `icons.light / dark-primary` | `#15272F` / `#D2D8DA` | Primary Icon Fill | `theme.palette.icon.default` | Canonical Theme Path | Standard icon fill (`src/theme/palette.ts`) | +| `status-colors.info` | `#2196F3` / `#2196F3` | System Info | `theme.palette.status.info` | Canonical Theme Path | Informative alerts and chips (`src/theme/palette.ts`) | +| `status-colors.success` | `#206D24` / `#206D24` | System Success | `theme.palette.status.success` | Canonical Theme Path | Confirmation badges and success alerts (`src/theme/palette.ts`) | +| `status-colors.warning` | `#F0A303` / `#F0A303` | System Warning | `theme.palette.status.warning` | Canonical Theme Path | Caution alerts and warnings (`src/theme/palette.ts`) | +| `status-colors.error` | `#F91313` / `#F91313` | System Error | `theme.palette.status.error` | Canonical Theme Path | Error alerts and destructive actions (`src/theme/palette.ts`) | +| `gradients.tint-light / dark` | Gradient | Header Tint Gradient | `theme.palette.surface.tint` | Canonical Theme Path | Modal headers and drawer banners (`src/theme/palette.ts`) | +| — | — | App Canvas (MUI default) | `theme.palette.background.default` | Legacy / Compatibility | Baseline MUI fallback canvas (`src/theme/palette.ts`; prefer `surface.primary` in new code) | +| — | `#FFFFFF` / `#3C494F` | Elevated Surface (Semantic) | `theme.palette.surface.elevated` | Canonical Theme Path | Semantic palette definition for elevated surfaces (`src/theme/palette.ts`, `src/theme/theme.ts`). Dark value resolves to `Colors.charcoal[40]` (`#3C494F`). Note: standard card widgets in Sistent map to `theme.palette.background.card` (`#212121` in dark mode); base `Card` (`src/base/Card/Card.tsx`) wraps MUI Card with default Paper elevation and radial wash (`src/theme/components/card.modifier.ts`). | +| — | — | MUI Primary Main | `theme.palette.primary.main` | Legacy / Compatibility | Baseline MUI primary (`src/theme/theme.ts`; prefer Sistent components or `interactive.primary`) | +| — | — | Brand Text | `theme.palette.text.brand` | Legacy / Compatibility | Older text brand property; prefer `theme.palette.interactive.primary` in new code (`src/theme/palette.ts`) | +| — | `rgba(0, 0, 0, 0.5)` | Un-focused Input Outline | `rgba(0, 0, 0, 0.5)` | Observed Implementation Literal | Hardcoded in `src/theme/components/outlinedinput.modifier.ts` and `src/theme/components/input.modifier.ts` | --- @@ -426,7 +426,7 @@ Sistent defines a cohesive scale of border radii for controls and containers. In | Formal Design Token | Design Value (Spec) | Implementation Mapping | Implementation Layer | Component Context / Evidence | | :------------------ | :------------------ | :----------------------------- | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------- | -| `rounded.xs` | `2px` | CSS literal (`2px`) | Observed Implementation Literal | Micro controls (`src/theme/components/checkbox.modifier.ts`) | +| `rounded.xs` | `2px` | CSS literal (`2px`) | Observed Implementation Literal | Micro controls (`src/icons/Checkbox/CheckboxIcon.tsx:28`) | | `rounded.sm` | `4px` | `theme.shape.borderRadius` | Canonical Theme Path | Standard controls: Buttons, TextFields, base Cards (`src/theme/theme.ts`) | | `rounded.md` | `5px` | CSS literal (`5px`) | Observed Implementation Literal | Modal card headers & wrappers (`src/custom/ModalCard/style.tsx`, `src/custom/Dialog/style.tsx`) | | `rounded.lg` | `8px` | CSS literal (`8px` / `0.5rem`) | Observed Implementation Literal | Modal dialog Paper (`src/custom/Modal/index.tsx`), floating panels (`src/custom/Panel/style.tsx`) | @@ -475,6 +475,9 @@ All 10 custom typography variants are registered in `MuiTypography` via `src/the `Button`, `Card`, `CardContent`, `CardHeader`, `CardMedia`, `Checkbox`, `Dialog`, `IconButton`, `Select`, `Tab`, `Tabs`, `TextField`, `Tooltip`, `Typography`. - **Custom Components**: Always import from `@sistent/sistent`: `ActionButton`, `CustomTooltip`, `DangerConfirmationModal`, `Modal`, `ModalBody`, `ModalFooter`, `Panel`, `ResponsiveDataTable`, `SearchBar`, `UniversalFilter`. +- **Dialog Abstraction Hierarchy**: + - `Modal` (with `ModalBody`, `ModalFooter`) is Sistent's primary high-level dialog abstraction featuring the brand gradient header (`surface.tint`), action buttons, and responsive fullscreen handling. + - Base `Dialog`, `DialogTitle`, `DialogContent`, and `DialogActions` (re-exported from `@sistent/sistent`) provide foundational primitives when custom or lower-level dialog composition is required. - **MUI Fallbacks**: Permitted only when building internal Sistent primitives or when no Sistent wrapper exists. --- @@ -524,6 +527,7 @@ Component-specific behaviors are never promoted to Canonical Design Rules withou - **Enforcement Status**: Contrast is guided by theme tokens and helper utilities (`readableTextColor`), but is not enforced by a global automated linting/test suite across every component. - **Label Capitalization**: Action labels use `textTransform: 'capitalize'` built into `textB2SemiBold`. - **Semantic ARIA**: Icon-only buttons must supply descriptive `aria-label` and `Tooltip`. +- **Modal Dialog Accessibility (`aria-labelledby`)**: Sistent's `Modal` component (`src/custom/Modal/index.tsx`) hardcodes `aria-labelledby="alert-dialog-slide-title"` and `aria-describedby="alert-dialog-slide-description"` on the underlying dialog, while its internal title element does not currently forward an `id`. To avoid duplicate-ID collisions across multiple dialog instances in the DOM, adding per-instance `titleId` / `descriptionId` prop forwarding (e.g. via `useId`) is tracked as an upcoming component enhancement. --- @@ -592,58 +596,6 @@ export const ProtectedActionButton: React.FC = ({ ); ``` -### Recipe 3: Modal Dialog with Sistent Gradient Header - -_Verified export_: `Modal`, `ModalBody`, `ModalFooter`, `Button` from `@sistent/sistent` (`src/custom/Modal/index.tsx`). - -```tsx -import React from 'react'; -import { Modal, ModalBody, ModalFooter, Button, Typography } from '@sistent/sistent'; -import { visuallyHidden } from '@mui/utils'; - -interface ConfirmationModalProps { - open: boolean; - title: string; - onClose: () => void; - onConfirm: () => void; - children: React.ReactNode; -} - -export const ConfirmationModal: React.FC = ({ - open, - title, - onClose, - onConfirm, - children -}) => ( - - - {/* Supplies the element matching Modal's hardcoded aria-labelledby="alert-dialog-slide-title" */} - - {title} - - {typeof children === 'string' ? ( - - {children} - - ) : ( -
{children}
- )} -
- - - - -
-); -``` - -> **Modal Accessibility Note (`aria-labelledby`)**: Sistent's `Modal` component internally hardcodes `aria-labelledby="alert-dialog-slide-title"` and `aria-describedby="alert-dialog-slide-description"` on the underlying dialog, which overrides any caller-provided `aria-label`. To ensure the dialog has a valid accessible name and description in the accessibility tree without duplicating visible text, callers must supply matching element IDs using a visually-hidden style (`sx={visuallyHidden}` from `@mui/utils` or standard visually-hidden CSS properties) within the modal body. - --- ## 9. AI Coding Guardrails (UI & Styling Specific) @@ -659,7 +611,7 @@ export const ConfirmationModal: React.FC = ({ 5. **Permission Rule**: > When an action is authorization-controlled, use the existing Sistent permission mechanism and provide the appropriate `permissionKey`. Do not invent local permission logic. 6. **No Manual Dark-Mode Forking**: - > Avoid manual conditionals like `theme.palette.mode === 'dark' ? '#212121' : '#fff'`. Use semantic tokens that resolve automatically (`theme.palette.background.card` when maintaining existing component bindings, `theme.palette.surface.elevated` for new semantic card surfaces, `theme.palette.surface.primary`, `theme.palette.text.default`). + > Avoid manual conditionals like `theme.palette.mode === 'dark' ? '#212121' : '#fff'`. Use semantic tokens that resolve automatically (`theme.palette.background.card` for card containers, `theme.palette.surface.primary` for application canvases, `theme.palette.text.default` for typography). 7. **Preserve Typography Variants**: > Use `` through `` rather than ad-hoc inline font declarations. @@ -675,7 +627,7 @@ When `DESIGN.md` and repository source code disagree: - **Implementation Drift**: A component diverges from established design tokens due to an accidental or ad-hoc local override. - **Intentional Legacy / Compatibility Behavior**: An older token path maintained to avoid breaking consumer downstream imports (e.g. `background.default`). - **Intentional Exception**: A component has a specialized visual requirement that intentionally diverges from the standard token. -3. **Record the discrepancy in the audit log.** +3. **Open a tracking issue to document the discrepancy for design-system maintainer review.** 4. **Only change the design contract when there is an explicit design-system decision to do so.** 5. **Do not make unrelated design changes during AI-context work.** From 70c7c9d2bdfffc393e99cfe4adac8d4b38533c5c Mon Sep 17 00:00:00 2001 From: Parth Gartan Date: Sat, 19 Sep 2026 06:52:26 +0000 Subject: [PATCH 4/4] docs: clarify borderRadius MUI default citation and modal a11y tracking Signed-off-by: Parth Gartan --- DESIGN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index eb814b9ee..72017fe6c 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -427,7 +427,7 @@ Sistent defines a cohesive scale of border radii for controls and containers. In | Formal Design Token | Design Value (Spec) | Implementation Mapping | Implementation Layer | Component Context / Evidence | | :------------------ | :------------------ | :----------------------------- | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------- | | `rounded.xs` | `2px` | CSS literal (`2px`) | Observed Implementation Literal | Micro controls (`src/icons/Checkbox/CheckboxIcon.tsx:28`) | -| `rounded.sm` | `4px` | `theme.shape.borderRadius` | Canonical Theme Path | Standard controls: Buttons, TextFields, base Cards (`src/theme/theme.ts`) | +| `rounded.sm` | `4px` | `theme.shape.borderRadius` | Canonical Theme Path | Standard controls: Buttons, TextFields, base Cards (MUI default; not overridden in `src/theme/theme.ts`) | | `rounded.md` | `5px` | CSS literal (`5px`) | Observed Implementation Literal | Modal card headers & wrappers (`src/custom/ModalCard/style.tsx`, `src/custom/Dialog/style.tsx`) | | `rounded.lg` | `8px` | CSS literal (`8px` / `0.5rem`) | Observed Implementation Literal | Modal dialog Paper (`src/custom/Modal/index.tsx`), floating panels (`src/custom/Panel/style.tsx`) | | `rounded.xl` | `15px` | CSS literal (`15px`) | Observed Implementation Literal | Featured card variants & image wells (`src/custom/CustomImage/CustomImage.tsx`) | @@ -527,7 +527,7 @@ Component-specific behaviors are never promoted to Canonical Design Rules withou - **Enforcement Status**: Contrast is guided by theme tokens and helper utilities (`readableTextColor`), but is not enforced by a global automated linting/test suite across every component. - **Label Capitalization**: Action labels use `textTransform: 'capitalize'` built into `textB2SemiBold`. - **Semantic ARIA**: Icon-only buttons must supply descriptive `aria-label` and `Tooltip`. -- **Modal Dialog Accessibility (`aria-labelledby`)**: Sistent's `Modal` component (`src/custom/Modal/index.tsx`) hardcodes `aria-labelledby="alert-dialog-slide-title"` and `aria-describedby="alert-dialog-slide-description"` on the underlying dialog, while its internal title element does not currently forward an `id`. To avoid duplicate-ID collisions across multiple dialog instances in the DOM, adding per-instance `titleId` / `descriptionId` prop forwarding (e.g. via `useId`) is tracked as an upcoming component enhancement. +- **Modal Dialog Accessibility (`aria-labelledby`)**: Sistent's `Modal` component (`src/custom/Modal/index.tsx`) hardcodes `aria-labelledby="alert-dialog-slide-title"` and `aria-describedby="alert-dialog-slide-description"` on the underlying dialog, while its internal title element does not currently forward an `id`. To avoid duplicate-ID collisions across multiple dialog instances in the DOM, adding per-instance `titleId` / `descriptionId` prop forwarding (e.g. via `useId`) is not yet tracked as a dedicated issue. ---