diff --git a/bun.lock b/bun.lock index 8678b3ac..3aa67c29 100644 --- a/bun.lock +++ b/bun.lock @@ -688,7 +688,7 @@ }, "packages/ui": { "name": "@wrnexus/ui", - "version": "0.8.24", + "version": "0.8.25", "dependencies": { "@wrnexus/core": "workspace:*", }, diff --git a/docs/ui-visual-contract-0.8.json b/docs/ui-visual-contract-0.8.json index 92ad76d0..9071dcc9 100644 --- a/docs/ui-visual-contract-0.8.json +++ b/docs/ui-visual-contract-0.8.json @@ -12,7 +12,7 @@ "packages/ui/components/Blockquote.wrn": "3761bf13babb6eb07ce7392258ee439ef5478064cf589eff63f945b950e00d2e", "packages/ui/components/Breadcrumb.wrn": "9f6c23550ebfc635c36ca8660953775170b4a471c0cd0206b9a7ba760994980a", "packages/ui/components/ButtonGroup.wrn": "fc23565ad56bd8483fcb777d9e02ccba2f9ea476c085754d42503bb83b718f72", - "packages/ui/components/CTASection.wrn": "d0665f2aa33eea95e6f84120f739b3dd9c8723c882138bcb1a7f80dcd7909904", + "packages/ui/components/CTASection.wrn": "f5289a08c14359e947eb49293f8a1ed36475bb07ff8c5247edc87c1469465de5", "packages/ui/components/Card.wrn": "3cc2ff238bda279c169026a462236144ad11ec0046deef00ab00e001a07e8a4c", "packages/ui/components/Carousel.wrn": "e7d921f802aa19210f3f415b59b70750a2c8d78e30684ad334809741c70a6bf9", "packages/ui/components/Chart.wrn": "07bc01247b5a1c2b82c2ba8386b7fcdbf480efead75d52b7da04d471072c7ee4", @@ -60,7 +60,7 @@ "packages/ui/components/MetricGrid.wrn": "1fcebf51dcbe8520fe16bda904c1ef206c25c3900438d53877492c64ea21a1a5", "packages/ui/components/Modal.wrn": "7fa4b877772736f29f690e24d8742922b310397ef3083f0b78acb67a9f0d14b2", "packages/ui/components/Nav.wrn": "c45b0ecb42250f4b3ede33c8932a025f789dda9ef48dbf332459ae458163e69a", - "packages/ui/components/Navbar.wrn": "1fafef0e0a87ca15547e338df2711634dab27dcde99263712a85f5d59b9f16a0", + "packages/ui/components/Navbar.wrn": "24dc14494fd48090da3545207926c295d3a6f1afd53c6ca26994dabca7a91ee0", "packages/ui/components/PageHeader.wrn": "321cde36ce8d521a57901033d46e8b437f42e558cca27f49ca26f7172aff1d54", "packages/ui/components/Pagination.wrn": "d54226705d4556f76ee5f0d6ae82d101ca6d006397756d547a9aa5954415ba39", "packages/ui/components/PinInput.wrn": "4dc398456f6392d7925db941debb484c7cb0358ecef527f696fe5ec4800a7602", diff --git a/packages/ui/COMPONENTS.md b/packages/ui/COMPONENTS.md index 73e51ed8..d079a424 100644 --- a/packages/ui/COMPONENTS.md +++ b/packages/ui/COMPONENTS.md @@ -45,7 +45,7 @@ Secure multi-cell PIN and verification-code input with regex and paste support. Theme-aware, responsive strong password component. - Mount: `data-component="StrongPassword"` -- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Password"`, `name: string = "password"`, `value: string = ""`, `placeholder: string = "Create a strong password"`, `autocomplete: string = "new-password"`, `minLength: number = 8`, `specialCharactersSet: string = "!@#$%^&*()_+-=[]{}|;:,.<>?"`, `requireLowercase: boolean = true`, `requireUppercase: boolean = true`, `requireNumber: boolean = true`, `requireSpecialCharacter: boolean = true`, `showRequirements: boolean = true`, `presentation: string = "inline"`, `hintText: string = "Use a unique password you do not use elsewhere."`, `emptyLabel: string = "Enter a password"`, `weakLabel: string = "Weak"`, `fairLabel: string = "Fair"`, `goodLabel: string = "Good"`, `strongLabel: string = "Strong"`, `disabled: boolean = false`, `readonly: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `validationMessage: string = ""`, `class: string = ""` +- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Password"`, `name: string = "password"`, `value: string = ""`, `placeholder: string = "Create a strong password"`, `autocomplete: string = "new-password"`, `minLength: number = 8`, `specialCharactersSet: string = "!@#$%^&*()_+-=[]{}|;:,.<>?"`, `requireLowercase: boolean = true`, `requireUppercase: boolean = true`, `requireNumber: boolean = true`, `requireSpecialCharacter: boolean = true`, `showRequirements: boolean = true`, `presentation: string = "inline"`, `hintText: string = "Use a unique password you do not use elsewhere."`, `emptyLabel: string = "Enter a password"`, `weakLabel: string = "Weak"`, `fairLabel: string = "Fair"`, `goodLabel: string = "Good"`, `strongLabel: string = "Strong"`, `disabled: boolean = false`, `readonly: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `validationMessage: string = ""`, `showLabel: string = "Show password"`, `hideLabel: string = "Hide password"`, `class: string = ""` - Slots: None - Outputs: `input({ value: string; score: number; maximumScore: number; percent: number; level: string })`, `change({ value: string; score: number; maximumScore: number; percent: number; level: string })`, `strength({ value: string; score: number; maximumScore: number; percent: number; level: string })` @@ -146,7 +146,7 @@ Theme-aware, responsive button group component. Group related content in a responsive themed surface with title, description, content, and supporting slots. - Mount: `data-component="Card"` -- Props: `title: string = "Card title"`, `subtitle: string = ""`, `description: string = ""`, `header: string = ""`, `footer: string = ""`, `imageSrc: string = ""`, `imageAlt: string = ""`, `imagePosition: string = "top"`, `actionLabel: string = ""`, `actionHref: string = ""`, `headerActions: unknown[] = []`, `navigation: unknown[] = []`, `activeNav: string = ""`, `mobileNavigation: boolean = false`, `alertTitle: string = ""`, `alertDescription: string = ""`, `empty: boolean = false`, `emptyTitle: string = "No data to show"`, `emptyIcon: string = "icon-[lucide--inbox]"`, `items: unknown[] = []`, `size: string = "md"`, `color: string = "primary"`, `variant: string = "default"`, `layout: string = "vertical"`, `align: string = "left"`, `hover: string = "none"`, `scrollable: boolean = false`, `maxHeight: string = "18rem"`, `dismissible: boolean = false`, `ariaLabel: string = ""`, `class: string = ""` +- Props: `title: string = "Card title"`, `titleKey: string = ""`, `subtitle: string = ""`, `subtitleKey: string = ""`, `description: string = ""`, `descriptionKey: string = ""`, `header: string = ""`, `footer: string = ""`, `imageSrc: string = ""`, `imageAlt: string = ""`, `imagePosition: string = "top"`, `actionLabel: string = ""`, `actionLabelKey: string = ""`, `actionHref: string = ""`, `headerActions: unknown[] = []`, `navigation: unknown[] = []`, `activeNav: string = ""`, `mobileNavigation: boolean = false`, `alertTitle: string = ""`, `alertDescription: string = ""`, `empty: boolean = false`, `emptyTitle: string = "No data to show"`, `emptyIcon: string = "icon-[lucide--inbox]"`, `items: unknown[] = []`, `size: string = "md"`, `color: string = "primary"`, `variant: string = "default"`, `layout: string = "vertical"`, `align: string = "left"`, `hover: string = "none"`, `scrollable: boolean = false`, `maxHeight: string = "18rem"`, `dismissible: boolean = false`, `ariaLabel: string = ""`, `class: string = ""` - Slots: `default` - Outputs: `click({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `action({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `navigate({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `dismiss({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `load({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `error({ error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)` @@ -606,7 +606,7 @@ Arrange feature cards, services, solutions, or benefits in a responsive equal-he Render structured responsive footer navigation, pre and post content, copyright content, links, and public events. - Mount: `data-component="Footer"` -- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Footer navigation"`, `items: unknown[] = []`, `columns: number = 3`, `maxWidth: string = "compact"`, `copyright: string = ""`, `class: string = ""` +- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Footer navigation"`, `items: unknown[] = []`, `columns: number = 3`, `maxWidth: string = "compact"`, `copyright: string = ""`, `copyrightKey: string = ""`, `translationPrefix: string = ""`, `class: string = ""` - Slots: `pre-footer`, `post-footer`, `copyright-left`, `copyright-right` - Outputs: `select({ value?: string | number | boolean | null; values?: Array; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `action({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })` @@ -707,7 +707,7 @@ Publish a responsive notice with badge, icon, supporting copy, action, dismiss b Close a page or major section with conversion-focused copy, actions, and optional supporting visual content. - Mount: `data-component="CTASection"` -- Props: `eyebrow: string = ""`, `title: string = "Ready to get started?"`, `description: string = ""`, `icon: string = ""`, `align: string = "center"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "solid"`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualPosition: string = "right"`, `maxWidth: string = "xl"`, `fullBleed: boolean = false`, `class: string = ""` +- Props: `eyebrow: string = ""`, `title: string = "Ready to get started?"`, `description: string = ""`, `icon: string = ""`, `align: string = "center"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "solid"`, `spacing: string = "none"`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualPosition: string = "right"`, `maxWidth: string = "xl"`, `fullBleed: boolean = false`, `class: string = ""` - Slots: `default`, `actions`, `visual`, `footer` - Outputs: None @@ -826,7 +826,7 @@ Theme-aware, responsive nav component. Theme-aware, responsive navbar component. - Mount: `data-component="Navbar"` -- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Primary navigation"`, `topbarLabel: string = "Utility navigation"`, `brand: Record = {}`, `items: unknown[] = []`, `actions: unknown[] = []`, `active: string = ""`, `sticky: boolean = false`, `openOnHover: boolean = false`, `maxWidth: string = "full"`, `mobileLabel: string = "Toggle navigation"`, `class: string = ""` +- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Primary navigation"`, `topbarLabel: string = "Utility navigation"`, `brand: Record = {}`, `items: unknown[] = []`, `actions: unknown[] = []`, `active: string = ""`, `sticky: boolean = false`, `openOnHover: boolean = false`, `maxWidth: string = "full"`, `mobileLabel: string = "Toggle navigation"`, `translationPrefix: string = ""`, `class: string = ""` - Slots: `topbar`, `actions` - Outputs: `toggle({ open: boolean })`, `open({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `close({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `select({ item: string | number | boolean | null | object; value: string | number | boolean; level: string })`, `action({ item: string | number | boolean | null | object; value: string | number | boolean })` diff --git a/packages/ui/component-reference.json b/packages/ui/component-reference.json index 1dbaad30..e027e6b7 100644 --- a/packages/ui/component-reference.json +++ b/packages/ui/component-reference.json @@ -2180,6 +2180,13 @@ "default": "\"solid\"", "options": [] }, + { + "name": "spacing", + "type": "string", + "required": false, + "default": "\"none\"", + "options": [] + }, { "name": "primaryLabel", "type": "string", @@ -2318,6 +2325,13 @@ "default": "\"Card title\"", "options": [] }, + { + "name": "titleKey", + "type": "string", + "required": false, + "default": "\"\"", + "options": [] + }, { "name": "subtitle", "type": "string", @@ -2325,6 +2339,13 @@ "default": "\"\"", "options": [] }, + { + "name": "subtitleKey", + "type": "string", + "required": false, + "default": "\"\"", + "options": [] + }, { "name": "description", "type": "string", @@ -2332,6 +2353,13 @@ "default": "\"\"", "options": [] }, + { + "name": "descriptionKey", + "type": "string", + "required": false, + "default": "\"\"", + "options": [] + }, { "name": "header", "type": "string", @@ -2374,6 +2402,13 @@ "default": "\"\"", "options": [] }, + { + "name": "actionLabelKey", + "type": "string", + "required": false, + "default": "\"\"", + "options": [] + }, { "name": "actionHref", "type": "string", @@ -6205,6 +6240,20 @@ "default": "\"\"", "options": [] }, + { + "name": "copyrightKey", + "type": "string", + "required": false, + "default": "\"\"", + "options": [] + }, + { + "name": "translationPrefix", + "type": "string", + "required": false, + "default": "\"\"", + "options": [] + }, { "name": "class", "type": "string", @@ -8899,6 +8948,13 @@ "default": "\"Toggle navigation\"", "options": [] }, + { + "name": "translationPrefix", + "type": "string", + "required": false, + "default": "\"\"", + "options": [] + }, { "name": "class", "type": "string", @@ -11844,6 +11900,20 @@ "default": "\"\"", "options": [] }, + { + "name": "showLabel", + "type": "string", + "required": false, + "default": "\"Show password\"", + "options": [] + }, + { + "name": "hideLabel", + "type": "string", + "required": false, + "default": "\"Hide password\"", + "options": [] + }, { "name": "class", "type": "string", diff --git a/packages/ui/components/CTASection.wrn b/packages/ui/components/CTASection.wrn index e0f02a6a..038fcd62 100644 --- a/packages/ui/components/CTASection.wrn +++ b/packages/ui/components/CTASection.wrn @@ -8,6 +8,7 @@ component CTASection { size: string = "default" color: string = "primary" variant: string = "solid" + spacing: string = "none" primaryLabel: string = "Get started" primaryHref: string = "#" primaryIcon: string = "" @@ -35,6 +36,7 @@ component CTASection { data-size='{size}' data-color='{color}' data-variant='{variant}' + data-spacing='{spacing}' data-max-width='{maxWidth}' data-full-bleed='{fullBleed ? "true" : "false"}' data-visual-position='{visualPosition}' @@ -186,6 +188,18 @@ component CTASection { --cta-contrast: var(--wrn-color-secondary-contrast); } + .wrn-cta-section[data-spacing="sm"] { + padding-block: 2rem; + } + + .wrn-cta-section[data-spacing="md"] { + padding-block: 3rem; + } + + .wrn-cta-section[data-spacing="lg"] { + padding-block: 4rem; + } + .wrn-cta-section[data-color="info"] { --cta-accent: var(--wrn-color-info); --cta-accent-hover: var(--wrn-color-info-hover); @@ -223,6 +237,20 @@ component CTASection { margin-inline: auto; } + @media (max-width: 640px) { + .wrn-cta-section[data-spacing="sm"] { + padding-block: 1.5rem; + } + + .wrn-cta-section[data-spacing="md"] { + padding-block: 2rem; + } + + .wrn-cta-section[data-spacing="lg"] { + padding-block: 2.5rem; + } + } + .wrn-cta-section[data-max-width="compact"] .wrn-cta-section__outer { width: min(calc(100% - 2rem), 64rem); } diff --git a/packages/ui/components/Navbar.wrn b/packages/ui/components/Navbar.wrn index d7855bd6..7089f2a5 100644 --- a/packages/ui/components/Navbar.wrn +++ b/packages/ui/components/Navbar.wrn @@ -170,12 +170,11 @@ size: string = "default" backdrop-filter: blur(16px); } - .wrn-navbar__topbar:empty, - .wrn-navbar__topbar:not(:has(*)) { + .wrn-navbar__topbar:not(:has(> *)) { display: none; } - .wrn-navbar__topbar:not(:empty) { + .wrn-navbar__topbar:has(> *) { display: flex; min-height: 2.25rem; align-items: center; diff --git a/packages/ui/package.json b/packages/ui/package.json index 267e71f2..cada755d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.8.24", + "version": "0.8.25", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/ui/test/ui-redesign-contract.test.ts b/packages/ui/test/ui-redesign-contract.test.ts index 6f87b815..20ec73de 100644 --- a/packages/ui/test/ui-redesign-contract.test.ts +++ b/packages/ui/test/ui-redesign-contract.test.ts @@ -118,6 +118,26 @@ describe("@wrnexus/ui redesign component pack", () => { } }); + test("Navbar only displays its utility bar when the topbar slot has rendered content", async () => { + const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8"); + + expect(source).toContain(".wrn-navbar__topbar:not(:has(> *))"); + expect(source).toContain(".wrn-navbar__topbar:has(> *)"); + expect(source).not.toContain(".wrn-navbar__topbar:not(:empty)"); + }); + + test("CTASection exposes responsive section spacing", async () => { + const source = await readFile(join(componentsDir, "CTASection.wrn"), "utf8"); + const ast = parse(source); + const defaults = new Map(ast.props.map((prop) => [prop.name, prop.default])); + + expect(defaults.get("spacing")).toBe('"none"'); + expect(source).toContain("data-spacing='{spacing}'"); + expect(source).toContain('.wrn-cta-section[data-spacing="sm"]'); + expect(source).toContain('.wrn-cta-section[data-spacing="md"]'); + expect(source).toContain('.wrn-cta-section[data-spacing="lg"]'); + }); + test("existing public defaults remain release compatible", async () => { const expectations: Record = { Container: [