fix(ui): collapse empty navbar topbar and add CTA spacing
Quality / quality (windows-latest) (push) Waiting to run
Quality / quality (ubuntu-latest) (push) Failing after 11m48s

This commit is contained in:
2026-08-24 18:28:39 +05:30
parent fa535575c5
commit 3b402b4e36
8 changed files with 129 additions and 12 deletions
+1 -1
View File
@@ -688,7 +688,7 @@
}, },
"packages/ui": { "packages/ui": {
"name": "@wrnexus/ui", "name": "@wrnexus/ui",
"version": "0.8.24", "version": "0.8.25",
"dependencies": { "dependencies": {
"@wrnexus/core": "workspace:*", "@wrnexus/core": "workspace:*",
}, },
+2 -2
View File
@@ -12,7 +12,7 @@
"packages/ui/components/Blockquote.wrn": "3761bf13babb6eb07ce7392258ee439ef5478064cf589eff63f945b950e00d2e", "packages/ui/components/Blockquote.wrn": "3761bf13babb6eb07ce7392258ee439ef5478064cf589eff63f945b950e00d2e",
"packages/ui/components/Breadcrumb.wrn": "9f6c23550ebfc635c36ca8660953775170b4a471c0cd0206b9a7ba760994980a", "packages/ui/components/Breadcrumb.wrn": "9f6c23550ebfc635c36ca8660953775170b4a471c0cd0206b9a7ba760994980a",
"packages/ui/components/ButtonGroup.wrn": "fc23565ad56bd8483fcb777d9e02ccba2f9ea476c085754d42503bb83b718f72", "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/Card.wrn": "3cc2ff238bda279c169026a462236144ad11ec0046deef00ab00e001a07e8a4c",
"packages/ui/components/Carousel.wrn": "e7d921f802aa19210f3f415b59b70750a2c8d78e30684ad334809741c70a6bf9", "packages/ui/components/Carousel.wrn": "e7d921f802aa19210f3f415b59b70750a2c8d78e30684ad334809741c70a6bf9",
"packages/ui/components/Chart.wrn": "07bc01247b5a1c2b82c2ba8386b7fcdbf480efead75d52b7da04d471072c7ee4", "packages/ui/components/Chart.wrn": "07bc01247b5a1c2b82c2ba8386b7fcdbf480efead75d52b7da04d471072c7ee4",
@@ -60,7 +60,7 @@
"packages/ui/components/MetricGrid.wrn": "1fcebf51dcbe8520fe16bda904c1ef206c25c3900438d53877492c64ea21a1a5", "packages/ui/components/MetricGrid.wrn": "1fcebf51dcbe8520fe16bda904c1ef206c25c3900438d53877492c64ea21a1a5",
"packages/ui/components/Modal.wrn": "7fa4b877772736f29f690e24d8742922b310397ef3083f0b78acb67a9f0d14b2", "packages/ui/components/Modal.wrn": "7fa4b877772736f29f690e24d8742922b310397ef3083f0b78acb67a9f0d14b2",
"packages/ui/components/Nav.wrn": "c45b0ecb42250f4b3ede33c8932a025f789dda9ef48dbf332459ae458163e69a", "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/PageHeader.wrn": "321cde36ce8d521a57901033d46e8b437f42e558cca27f49ca26f7172aff1d54",
"packages/ui/components/Pagination.wrn": "d54226705d4556f76ee5f0d6ae82d101ca6d006397756d547a9aa5954415ba39", "packages/ui/components/Pagination.wrn": "d54226705d4556f76ee5f0d6ae82d101ca6d006397756d547a9aa5954415ba39",
"packages/ui/components/PinInput.wrn": "4dc398456f6392d7925db941debb484c7cb0358ecef527f696fe5ec4800a7602", "packages/ui/components/PinInput.wrn": "4dc398456f6392d7925db941debb484c7cb0358ecef527f696fe5ec4800a7602",
+5 -5
View File
@@ -45,7 +45,7 @@ Secure multi-cell PIN and verification-code input with regex and paste support.
Theme-aware, responsive strong password component. Theme-aware, responsive strong password component.
- Mount: `data-component="StrongPassword"` - 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 - 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 })` - 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. Group related content in a responsive themed surface with title, description, content, and supporting slots.
- Mount: `data-component="Card"` - 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` - 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)` - 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. Render structured responsive footer navigation, pre and post content, copyright content, links, and public events.
- Mount: `data-component="Footer"` - 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` - Slots: `pre-footer`, `post-footer`, `copyright-left`, `copyright-right`
- Outputs: `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `action({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })` - Outputs: `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; 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. Close a page or major section with conversion-focused copy, actions, and optional supporting visual content.
- Mount: `data-component="CTASection"` - 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` - Slots: `default`, `actions`, `visual`, `footer`
- Outputs: None - Outputs: None
@@ -826,7 +826,7 @@ Theme-aware, responsive nav component.
Theme-aware, responsive navbar component. Theme-aware, responsive navbar component.
- Mount: `data-component="Navbar"` - Mount: `data-component="Navbar"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Primary navigation"`, `topbarLabel: string = "Utility navigation"`, `brand: Record<string, unknown> = {}`, `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<string, unknown> = {}`, `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` - 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 })` - 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 })`
+70
View File
@@ -2180,6 +2180,13 @@
"default": "\"solid\"", "default": "\"solid\"",
"options": [] "options": []
}, },
{
"name": "spacing",
"type": "string",
"required": false,
"default": "\"none\"",
"options": []
},
{ {
"name": "primaryLabel", "name": "primaryLabel",
"type": "string", "type": "string",
@@ -2318,6 +2325,13 @@
"default": "\"Card title\"", "default": "\"Card title\"",
"options": [] "options": []
}, },
{
"name": "titleKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "subtitle", "name": "subtitle",
"type": "string", "type": "string",
@@ -2325,6 +2339,13 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "subtitleKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "description", "name": "description",
"type": "string", "type": "string",
@@ -2332,6 +2353,13 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "descriptionKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "header", "name": "header",
"type": "string", "type": "string",
@@ -2374,6 +2402,13 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "actionLabelKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "actionHref", "name": "actionHref",
"type": "string", "type": "string",
@@ -6205,6 +6240,20 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "copyrightKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "translationPrefix",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -8899,6 +8948,13 @@
"default": "\"Toggle navigation\"", "default": "\"Toggle navigation\"",
"options": [] "options": []
}, },
{
"name": "translationPrefix",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -11844,6 +11900,20 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "showLabel",
"type": "string",
"required": false,
"default": "\"Show password\"",
"options": []
},
{
"name": "hideLabel",
"type": "string",
"required": false,
"default": "\"Hide password\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
+28
View File
@@ -8,6 +8,7 @@ component CTASection {
size: string = "default" size: string = "default"
color: string = "primary" color: string = "primary"
variant: string = "solid" variant: string = "solid"
spacing: string = "none"
primaryLabel: string = "Get started" primaryLabel: string = "Get started"
primaryHref: string = "#" primaryHref: string = "#"
primaryIcon: string = "" primaryIcon: string = ""
@@ -35,6 +36,7 @@ component CTASection {
data-size='{size}' data-size='{size}'
data-color='{color}' data-color='{color}'
data-variant='{variant}' data-variant='{variant}'
data-spacing='{spacing}'
data-max-width='{maxWidth}' data-max-width='{maxWidth}'
data-full-bleed='{fullBleed ? "true" : "false"}' data-full-bleed='{fullBleed ? "true" : "false"}'
data-visual-position='{visualPosition}' data-visual-position='{visualPosition}'
@@ -186,6 +188,18 @@ component CTASection {
--cta-contrast: var(--wrn-color-secondary-contrast); --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"] { .wrn-cta-section[data-color="info"] {
--cta-accent: var(--wrn-color-info); --cta-accent: var(--wrn-color-info);
--cta-accent-hover: var(--wrn-color-info-hover); --cta-accent-hover: var(--wrn-color-info-hover);
@@ -223,6 +237,20 @@ component CTASection {
margin-inline: auto; 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 { .wrn-cta-section[data-max-width="compact"] .wrn-cta-section__outer {
width: min(calc(100% - 2rem), 64rem); width: min(calc(100% - 2rem), 64rem);
} }
+2 -3
View File
@@ -170,12 +170,11 @@ size: string = "default"
backdrop-filter: blur(16px); backdrop-filter: blur(16px);
} }
.wrn-navbar__topbar:empty, .wrn-navbar__topbar:not(:has(> *)) {
.wrn-navbar__topbar:not(:has(*)) {
display: none; display: none;
} }
.wrn-navbar__topbar:not(:empty) { .wrn-navbar__topbar:has(> *) {
display: flex; display: flex;
min-height: 2.25rem; min-height: 2.25rem;
align-items: center; align-items: center;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/ui", "name": "@wrnexus/ui",
"version": "0.8.24", "version": "0.8.25",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.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 () => { test("existing public defaults remain release compatible", async () => {
const expectations: Record<string, string[]> = { const expectations: Record<string, string[]> = {
Container: [ Container: [