From 53030891c6bae10abe9e8bdf4ef3ed6acc3bb925 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Sun, 26 Apr 2026 13:30:03 -0400 Subject: [PATCH 1/3] feat(ui/design-system): codify rail-anchored layout invariants and palette MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a page-level design system spec for CCS dashboard Config pages, without migrating any production page yet. Pure-spec PR: docs + primitive treatments + DEV-only /_styleguide route. Production page adoption is intentionally left for follow-up work, one page at a time. Spec — `ui/docs/design-system.md`: - §0 Layout invariants (NEVER VIOLATE): two-column shell, no second horizontal strip below the global topbar, sibling-pane top alignment, cliproxy as canonical reference, user-resizable form/json split, and content-fit unified rail (`w-fit min-w-[240px] max-w-[360px]`) - §1 identity-strip patterns: HeroBar (home), rail-anchored (cliproxy), PageHeader (Monitor-only), Bespoke (health) - §2 body archetypes: Config (rail + form + optional json) and Monitor - §4 anti-patterns with rejected code samples (PageHeader-over- ConfigLayout, tab-bar offsetting json pane, redundant description band, blank vertical band) - §5 color & accent rules using the existing Pampas/Crail palette only: 1-accent-dot rule, sibling-pane differentiation, status pill spec, three-tier intensity ladder, sensitive-field treatment per §5g Decisions log — `ui/docs/design-decisions.md`: - v1.6: content-fit rail (replaces fixed 260px) - v1.5: color & accent treatment for body panes - v1.4: resizable form/json split - v1.3: layout invariants and rail-anchored default for Config Primitives — `ui/src/components/config-layout/`: - ConfigLayout: rail rendered as content-fit ` )} - +
{form}
diff --git a/ui/src/components/forms/field.tsx b/ui/src/components/forms/field.tsx index c8abe7ec..efb9ffd8 100644 --- a/ui/src/components/forms/field.tsx +++ b/ui/src/components/forms/field.tsx @@ -44,8 +44,9 @@ export function Field({ diff --git a/ui/src/components/ui/masked-input.tsx b/ui/src/components/ui/masked-input.tsx index e5242a30..57f8ad18 100644 --- a/ui/src/components/ui/masked-input.tsx +++ b/ui/src/components/ui/masked-input.tsx @@ -3,7 +3,10 @@ import { Eye, EyeOff, Lock } from 'lucide-react'; import { Input } from '@/components/ui/input'; import { cn } from '@/lib/utils'; -interface MaskedInputProps extends React.InputHTMLAttributes { +// `type` is intentionally omitted from the prop surface so callers cannot +// override the `password`/`text` toggle and accidentally render a credential +// in plaintext. The component is the sole authority on the input type. +interface MaskedInputProps extends Omit, 'type'> { label?: string; /** * Hide the "sensitive" pill on the label row. Defaults to false (pill shown). @@ -48,20 +51,23 @@ export function MaskedInput({ )}
diff --git a/ui/src/pages/_styleguide.tsx b/ui/src/pages/_styleguide.tsx index 3fb875cc..cc25bb8d 100644 --- a/ui/src/pages/_styleguide.tsx +++ b/ui/src/pages/_styleguide.tsx @@ -210,7 +210,7 @@ function Intro() {

Full spec: ui/docs/design-system.md · revision history:{' '} - ui/docs/design-decisions.md (current: v1.7, 2026-04-26). Color rules: §5. + ui/docs/design-decisions.md (current: v1.8, 2026-04-26). Color rules: §5.

);