@ds-mo/ui 14.12.0 → 14.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +10 -0
- package/dist/.package-ready.json +1 -1
- package/dist/agent-patterns.json +1 -1
- package/dist/agent.json +89 -2
- package/dist/angular/ds-paper-texture.d.ts +12 -0
- package/dist/angular/ds-paper-texture.js +38 -0
- package/dist/angular/ds-shell-app.d.ts +1 -1
- package/dist/angular/ds-shell-app.js +3 -3
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +1 -0
- package/dist/angular/proxies.d.ts +1 -0
- package/dist/angular/proxies.js +1 -0
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-title.js +1 -1
- package/dist/components/ds-bar-workflow.js +1 -1
- package/dist/components/ds-bar-workflow.js.map +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-mobile-header.js +1 -1
- package/dist/components/ds-mobile-section-switcher.js +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-paper-texture.d.ts +11 -0
- package/dist/components/ds-paper-texture.js +2 -0
- package/dist/components/ds-paper-texture.js.map +1 -0
- package/dist/components/ds-shell-app.js +1 -1
- package/dist/components/ds-shell-app.js.map +1 -1
- package/dist/components/ds-shell-page.js +1 -1
- package/dist/components/ds-shell-tools.js +1 -1
- package/dist/components/ds-swatch-picker.js +1 -1
- package/dist/components/ds-table-saved-views.js +1 -1
- package/dist/components/ds-table-saved-views.js.map +1 -1
- package/dist/components/ds-table-toolbar.js +1 -1
- package/dist/components/ds-table.js +1 -1
- package/dist/components/ds-toast.js +1 -1
- package/dist/components/ds-typing-indicator.js +1 -1
- package/dist/components/{p-BAStZzJu.js → p-C2n8A8NA.js} +2 -2
- package/dist/components/p-C5eosu67.js +2 -0
- package/dist/components/p-C5eosu67.js.map +1 -0
- package/dist/components/{p-BHNFoHay.js → p-CWDmHPJC.js} +2 -2
- package/dist/components/{p-Bubs1_8P.js → p-Ck_VRQYi.js} +2 -2
- package/dist/components/{p-BZ3UTshV.js → p-Cyw9odJX.js} +2 -2
- package/dist/components/p-DIEx1e0M.js +2 -0
- package/dist/components/p-DIEx1e0M.js.map +1 -0
- package/dist/components/{p-DjqZWBnM.js → p-DL_5VENG.js} +2 -2
- package/dist/components/{p-Dh_p8OzP.js → p-DfyfrfHL.js} +2 -2
- package/dist/components/{p-DgWGfscR.js → p-Rfpd5mWO.js} +2 -2
- package/dist/components/p-URdgCwEs.js +2 -0
- package/dist/components/{p-GdlSIpHV.js.map → p-URdgCwEs.js.map} +1 -1
- package/dist/docs/components.json +101 -1
- package/dist/mcp-data/registry/bar-nav.json +1 -1
- package/dist/mcp-data/registry/panel-nav.json +1 -1
- package/dist/mcp-data/registry/paper-texture.json +141 -0
- package/dist/mcp-data/registry/registry.json +91 -1
- package/dist/mcp-data/registry/shell-app.json +25 -4
- package/dist/react/components.d.ts +1 -0
- package/dist/react/components.js +1 -0
- package/dist/react/ds-paper-texture.d.ts +9 -0
- package/dist/react/ds-paper-texture.js +12 -0
- package/dist/types/components/PaperTexture/PaperTexture.d.ts +18 -0
- package/dist/types/components/PaperTexture/paper-texture-types.d.ts +39 -0
- package/dist/types/components/ShellApp/ShellApp.d.ts +7 -0
- package/dist/types/components.d.ts +33 -0
- package/package.json +4 -2
- package/dist/components/p-DSuAHQRk.js +0 -2
- package/dist/components/p-DSuAHQRk.js.map +0 -1
- package/dist/components/p-GdlSIpHV.js +0 -2
- /package/dist/components/{p-BAStZzJu.js.map → p-C2n8A8NA.js.map} +0 -0
- /package/dist/components/{p-BHNFoHay.js.map → p-CWDmHPJC.js.map} +0 -0
- /package/dist/components/{p-Bubs1_8P.js.map → p-Ck_VRQYi.js.map} +0 -0
- /package/dist/components/{p-BZ3UTshV.js.map → p-Cyw9odJX.js.map} +0 -0
- /package/dist/components/{p-DjqZWBnM.js.map → p-DL_5VENG.js.map} +0 -0
- /package/dist/components/{p-Dh_p8OzP.js.map → p-DfyfrfHL.js.map} +0 -0
- /package/dist/components/{p-DgWGfscR.js.map → p-Rfpd5mWO.js.map} +0 -0
|
@@ -296,7 +296,7 @@
|
|
|
296
296
|
"files": [
|
|
297
297
|
{
|
|
298
298
|
"path": "src/wc/components/BarNav/BarNav.css",
|
|
299
|
-
"content": "@import '../../utils/control-density.css';\n@import '../../utils/control-parts.css';\n@import '../../utils/chrome-layout.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/control-inactive.css';\n\n:host {\n display: block;\n}\n\n.bar-nav {\n position: relative;\n height: var(--dimension-size-600);\n border-bottom: var(--dimension-stroke-width-012) solid var(--_bar-nav-border);\n background-color: var(--_bar-nav-bg);\n color: var(--_bar-nav-fg-primary);\n -webkit-user-select: none;\n user-select: none;\n overflow: hidden;\n transition:\n background-color var(--effect-motion-short-3),\n border-color var(--effect-motion-short-3),\n color var(--effect-motion-short-3);\n isolation: isolate;\n /* Default chrome — app surfaces */\n --_bar-nav-bg: var(--color-background-secondary);\n --_bar-nav-border: var(--color-border-tertiary);\n --_bar-nav-fg-primary: var(--color-foreground-primary);\n --_bar-nav-fg-secondary: var(--color-foreground-secondary);\n --_bar-nav-hover: var(--color-interaction-hover);\n --_bar-nav-pressed: var(--color-interaction-pressed);\n --_bar-nav-focus: var(--color-interaction-focus);\n --_bar-nav-dot: var(--color-foreground-bold-brand);\n --ds-focus-ring-color: var(--_bar-nav-focus);\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers later. */\n.bar-nav--dashboard,\n.bar-nav--settings {\n --_bar-nav-bg: var(--color-background-secondary);\n --_bar-nav-border: var(--color-border-tertiary);\n --_bar-nav-fg-primary: var(--color-foreground-primary);\n --_bar-nav-fg-secondary: var(--color-foreground-secondary);\n --_bar-nav-hover: var(--color-interaction-hover);\n --_bar-nav-pressed: var(--color-interaction-pressed);\n --_bar-nav-focus: var(--color-interaction-focus);\n --_bar-nav-dot: var(--color-foreground-bold-brand);\n}\n\n/* Shell-owned chrome (bg + wash) — bar is a transparent frame. */\n:host-context(ds-shell-app:not([gradient-preset='none'])) .bar-nav {\n background-color: transparent;\n}\n\n.bar-nav > :not(.bar-nav__tabs-probe) {\n position: relative;\n z-index: 3;\n}\n\n/* Overflow — keep fitting tabs visible and pin the overflow trigger to the far edge. */\n.bar-nav--tabs-collapsed {\n display: flex;\n gap: var(--dimension-space-100);\n align-items: center;\n}\n\n/* Expanded tabs — consume full header width */\n.bar-nav__left {\n position: relative;\n flex: 1 1 0%;\n min-width: 0;\n width: 0;\n display: flex;\n align-items: center;\n}\n\n.bar-nav__tabs-visible {\n flex: 1;\n min-width: 0;\n width: 100%;\n}\n\n/* Unconstrained width so intrinsic tab row width is measurable while collapsed */\n.bar-nav__tabs-probe {\n position: absolute;\n left: 0;\n top: 0;\n visibility: hidden;\n pointer-events: none;\n overflow: visible;\n width: max-content;\n max-width: none;\n z-index: -1;\n}\n\n.bar-nav__tabs-probe > div {\n width: max-content;\n}\n\n.bar-nav__tab-list {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n position: relative;\n width: max-content;\n}\n\n.bar-nav__tab {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n height: var(--ds-control-height);\n min-width: var(--ds-control-height);\n padding: 0 var(--ds-control-padding-inline);\n box-sizing: border-box;\n border: none;\n border-radius: var(--ds-control-radius);\n background-color: transparent;\n color: var(--_bar-nav-fg-secondary);\n cursor: default;\n position: relative;\n flex-shrink: 0;\n -webkit-user-select: none;\n user-select: none;\n transition: color var(--effect-motion-short-2);\n}\n\n.bar-nav__tab::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--ds-control-radius);\n background: transparent;\n pointer-events: none;\n cursor: inherit;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .bar-nav__tab:hover::after {\n background: var(--_bar-nav-hover);\n }\n}\n\n.bar-nav__tab:active::after {\n background: var(--_bar-nav-pressed);\n}\n\n.bar-nav__tab--selected {\n color: var(--_bar-nav-fg-primary);\n}\n\n.bar-nav__tab-label {\n display: inline-flex;\n align-items: center;\n white-space: nowrap;\n}\n\n.bar-nav__tab-divider {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n width: var(--dimension-space-100);\n height: var(--ds-control-height);\n padding-block: var(--ds-control-padding-inline);\n flex-shrink: 0;\n pointer-events: none;\n}\n\n.bar-nav__tab-divider-line {\n width: var(--dimension-stroke-width-012);\n align-self: stretch;\n background-color: var(--_bar-nav-border);\n}\n\n/* In-flow placeholder while measuring overflow — avoids layout jump; hidden from view/accessibility. */\n.bar-nav__tabs-pending {\n flex: 1;\n min-width: 0;\n height: var(--ds-control-height);\n visibility: hidden;\n pointer-events: none;\n}\n\n/* Overflow trigger — compact icon button aligned to the right edge. */\n.bar-nav__overflow-trigger {\n margin-left: auto;\n --ds-button-unfilled-fg: var(--_bar-nav-fg-secondary);\n --ds-button-unfilled-fg-active: var(--_bar-nav-fg-primary);\n --ds-interaction-hover: var(--_bar-nav-hover);\n --ds-interaction-pressed: var(--_bar-nav-pressed);\n --ds-focus-ring-color: var(--_bar-nav-focus);\n}\n\n/* Fallback heading when no tabs — matches a single (selected) tab so the area\n name shares the tab label's type token, color, height and horizontal inset.\n Inset = tab button padding (space-075, 6px) + label inset (space-025, 2px).\n Color comes from ds-text color=\"primary\"; beat Text's inline-box display. */\nds-text.bar-nav__heading {\n display: inline-flex;\n align-items: center;\n height: var(--ds-control-height);\n padding-inline: calc(var(--ds-control-padding-inline) + var(--ds-control-label-inset));\n -webkit-user-select: none;\n user-select: none;\n}\n",
|
|
299
|
+
"content": "@import '../../utils/control-density.css';\n@import '../../utils/control-parts.css';\n@import '../../utils/chrome-layout.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/control-inactive.css';\n\n:host {\n display: block;\n}\n\n.bar-nav {\n position: relative;\n height: var(--dimension-size-600);\n border-bottom: var(--dimension-stroke-width-012) solid var(--_bar-nav-border);\n background-color: var(--_bar-nav-bg);\n color: var(--_bar-nav-fg-primary);\n -webkit-user-select: none;\n user-select: none;\n overflow: hidden;\n transition:\n background-color var(--effect-motion-short-3),\n border-color var(--effect-motion-short-3),\n color var(--effect-motion-short-3);\n isolation: isolate;\n /* Default chrome — app surfaces */\n --_bar-nav-bg: var(--color-background-secondary);\n --_bar-nav-border: var(--color-border-tertiary);\n --_bar-nav-fg-primary: var(--color-foreground-primary);\n --_bar-nav-fg-secondary: var(--color-foreground-secondary);\n --_bar-nav-hover: var(--color-interaction-hover);\n --_bar-nav-pressed: var(--color-interaction-pressed);\n --_bar-nav-focus: var(--color-interaction-focus);\n --_bar-nav-dot: var(--color-foreground-bold-brand);\n --ds-focus-ring-color: var(--_bar-nav-focus);\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers later. */\n.bar-nav--dashboard,\n.bar-nav--settings {\n --_bar-nav-bg: var(--color-background-secondary);\n --_bar-nav-border: var(--color-border-tertiary);\n --_bar-nav-fg-primary: var(--color-foreground-primary);\n --_bar-nav-fg-secondary: var(--color-foreground-secondary);\n --_bar-nav-hover: var(--color-interaction-hover);\n --_bar-nav-pressed: var(--color-interaction-pressed);\n --_bar-nav-focus: var(--color-interaction-focus);\n --_bar-nav-dot: var(--color-foreground-bold-brand);\n}\n\n/* Shell-owned chrome (bg + wash) — bar is a transparent frame. */\n:host-context(ds-shell-app:not([gradient-preset='none'])) .bar-nav,\n:host-context(ds-shell-app.shell-app--paper-texture) .bar-nav {\n background-color: transparent;\n}\n\n.bar-nav > :not(.bar-nav__tabs-probe) {\n position: relative;\n z-index: 3;\n}\n\n/* Overflow — keep fitting tabs visible and pin the overflow trigger to the far edge. */\n.bar-nav--tabs-collapsed {\n display: flex;\n gap: var(--dimension-space-100);\n align-items: center;\n}\n\n/* Expanded tabs — consume full header width */\n.bar-nav__left {\n position: relative;\n flex: 1 1 0%;\n min-width: 0;\n width: 0;\n display: flex;\n align-items: center;\n}\n\n.bar-nav__tabs-visible {\n flex: 1;\n min-width: 0;\n width: 100%;\n}\n\n/* Unconstrained width so intrinsic tab row width is measurable while collapsed */\n.bar-nav__tabs-probe {\n position: absolute;\n left: 0;\n top: 0;\n visibility: hidden;\n pointer-events: none;\n overflow: visible;\n width: max-content;\n max-width: none;\n z-index: -1;\n}\n\n.bar-nav__tabs-probe > div {\n width: max-content;\n}\n\n.bar-nav__tab-list {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n position: relative;\n width: max-content;\n}\n\n.bar-nav__tab {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n height: var(--ds-control-height);\n min-width: var(--ds-control-height);\n padding: 0 var(--ds-control-padding-inline);\n box-sizing: border-box;\n border: none;\n border-radius: var(--ds-control-radius);\n background-color: transparent;\n color: var(--_bar-nav-fg-secondary);\n cursor: default;\n position: relative;\n flex-shrink: 0;\n -webkit-user-select: none;\n user-select: none;\n transition: color var(--effect-motion-short-2);\n}\n\n.bar-nav__tab::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--ds-control-radius);\n background: transparent;\n pointer-events: none;\n cursor: inherit;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .bar-nav__tab:hover::after {\n background: var(--_bar-nav-hover);\n }\n}\n\n.bar-nav__tab:active::after {\n background: var(--_bar-nav-pressed);\n}\n\n.bar-nav__tab--selected {\n color: var(--_bar-nav-fg-primary);\n}\n\n.bar-nav__tab-label {\n display: inline-flex;\n align-items: center;\n white-space: nowrap;\n}\n\n.bar-nav__tab-divider {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n width: var(--dimension-space-100);\n height: var(--ds-control-height);\n padding-block: var(--ds-control-padding-inline);\n flex-shrink: 0;\n pointer-events: none;\n}\n\n.bar-nav__tab-divider-line {\n width: var(--dimension-stroke-width-012);\n align-self: stretch;\n background-color: var(--_bar-nav-border);\n}\n\n/* In-flow placeholder while measuring overflow — avoids layout jump; hidden from view/accessibility. */\n.bar-nav__tabs-pending {\n flex: 1;\n min-width: 0;\n height: var(--ds-control-height);\n visibility: hidden;\n pointer-events: none;\n}\n\n/* Overflow trigger — compact icon button aligned to the right edge. */\n.bar-nav__overflow-trigger {\n margin-left: auto;\n --ds-button-unfilled-fg: var(--_bar-nav-fg-secondary);\n --ds-button-unfilled-fg-active: var(--_bar-nav-fg-primary);\n --ds-interaction-hover: var(--_bar-nav-hover);\n --ds-interaction-pressed: var(--_bar-nav-pressed);\n --ds-focus-ring-color: var(--_bar-nav-focus);\n}\n\n/* Fallback heading when no tabs — matches a single (selected) tab so the area\n name shares the tab label's type token, color, height and horizontal inset.\n Inset = tab button padding (space-075, 6px) + label inset (space-025, 2px).\n Color comes from ds-text color=\"primary\"; beat Text's inline-box display. */\nds-text.bar-nav__heading {\n display: inline-flex;\n align-items: center;\n height: var(--ds-control-height);\n padding-inline: calc(var(--ds-control-padding-inline) + var(--ds-control-label-inset));\n -webkit-user-select: none;\n user-select: none;\n}\n",
|
|
300
300
|
"type": "registry:ui"
|
|
301
301
|
},
|
|
302
302
|
{
|
|
@@ -620,7 +620,7 @@
|
|
|
620
620
|
"files": [
|
|
621
621
|
{
|
|
622
622
|
"path": "src/wc/components/PanelNav/PanelNav.css",
|
|
623
|
-
"content": "@import '../../utils/control-density.css';\n@import '../../utils/chrome-layout.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/scrollbar-hidden.css';\n\n/* ── Theme variables ──────────────────────────────────────────────────────── */\n\n.panel-nav {\n /* ─ Sizes ─ */\n --_nav-width: var(--dimension-panel-width-2xs);\n --_nav-width-collapsed: var(--dimension-size-600);\n --_nav-item-height: var(--dimension-size-400, 32px);\n --_nav-padding-x: var(--dimension-space-100, 8px);\n --_nav-padding-y: var(--dimension-space-100, 8px);\n /* gear button outer width = padding*2 + icon */\n --_nav-footer-btn-w: calc(var(--dimension-space-075, 6px) * 2 + var(--dimension-iconography-md, 20px));\n\n /* ─ Motion ─ */\n /* --ds-panel-nav-speed: unitless multiplier; override in Storybook to slow-mo (e.g. 10 = 10×).\n *-dur vars are duration-only (needed for calc); *-motion vars add easing for use in transitions. */\n --_nav-speed: var(--ds-panel-nav-speed, 1);\n --_nav-expand-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-expand-motion: var(--_nav-expand-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-motion: var(--_nav-label-fade-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-out-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-out-motion: var(--_nav-label-fade-out-dur) var(--effect-animation-easing-ease-in-out, ease);\n\n /* ─ Colours – default chrome (app surfaces) ─ */\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Default chrome overrides ────────────────────────────────────────────── */\n\n/* Shell-owned chrome (bg + wash) — panel is a transparent frame. */\n:host-context(ds-shell-app:not([gradient-preset='none'])) .panel-nav {\n background-color: transparent;\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers on these classes later. */\n.panel-nav--dashboard,\n.panel-nav--settings {\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Shell ───────────────────────────────────────────────────────────────── */\n\n.panel-nav {\n display: flex;\n flex-direction: column;\n position: relative;\n width: var(--_nav-width);\n min-width: var(--_nav-width);\n height: 100%;\n background-color: var(--_nav-bg);\n border-right: var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);\n box-sizing: border-box;\n overflow: visible;\n transition:\n width var(--_nav-expand-motion),\n min-width var(--_nav-expand-motion),\n background-color var(--effect-motion-short-3, 200ms ease),\n border-color var(--effect-motion-short-3, 200ms ease);\n font-family: var(--typography-font-family-ui, system-ui);\n isolation: isolate;\n}\n\n.panel-nav__header,\n.panel-nav__body,\n.panel-nav__footer {\n position: relative;\n z-index: 3;\n}\n\n.panel-nav--collapsed {\n width: var(--_nav-width-collapsed);\n min-width: var(--_nav-width-collapsed);\n}\n\n/* ── Header ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__header {\n height: var(--dimension-size-600, 48px);\n flex-shrink: 0;\n border-bottom: var(--dimension-stroke-width-012, 1px) solid var(--_nav-divider);\n transition: border-color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav__header-btn {\n all: unset;\n /* Re-assert after unset — required for `.ds-interaction-fill` stacking. */\n position: relative;\n z-index: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--dimension-space-075, 6px);\n border-radius: var(--ds-control-radius, var(--dimension-radius-025, 2px));\n cursor: default;\n color: var(--_nav-fg-primary);\n flex-shrink: 0;\n box-sizing: border-box;\n transition: color var(--effect-motion-short-3, 200ms ease);\n /* Hover/press: `.ds-interaction-fill` (tokens from --_nav-* via --ds-interaction-*). */\n}\n\n.panel-nav--breakpoint-locked .panel-nav__header-btn {\n cursor: default;\n}\n\n/* Logo — visible by default; instant swap to toggle on hover (no crossfade overlap) */\n.panel-nav__header-logo {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 2;\n opacity: 1;\n}\n\n.panel-nav__m-mark {\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n display: block;\n}\n\n/* Toggle icon — stacked on logo; under hover wash (::after z-index 3) */\n.panel-nav__header-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n inset: 0;\n z-index: 2;\n opacity: 0;\n pointer-events: none;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .panel-nav:not(.panel-nav--breakpoint-locked)\n .panel-nav__header-btn:hover\n .panel-nav__header-logo {\n opacity: 0;\n }\n\n .panel-nav:not(.panel-nav--breakpoint-locked)\n .panel-nav__header-btn:hover\n .panel-nav__header-toggle {\n opacity: 1;\n }\n}\n\n/* ── Scrollable body ─────────────────────────────────────────────────────── */\n\n.panel-nav__body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.panel-nav__sections {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-200, 16px);\n width: 100%;\n}\n\n/* ── Group ───────────────────────────────────────────────────────────────── */\n\n.panel-nav__group {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050, 4px);\n}\n\n/*\n * Section headers — typography via ds-text (text-caption + emphasis).\n * Element selector so display:flex beats Text's inline-block, and padding\n * isn't clobbered by sharing `.panel-nav__item-label-text` on the same host.\n */\nds-text.panel-nav__group-label {\n display: flex;\n align-items: center;\n box-sizing: border-box;\n text-transform: uppercase;\n color: var(--_nav-fg-secondary);\n user-select: none;\n height: var(--dimension-size-300, 24px);\n /* Former nested pad: group-label (075) + item-label-text (025). */\n padding: 0 calc(var(--dimension-space-075, 6px) + var(--ds-control-label-inset));\n white-space: nowrap;\n overflow: hidden;\n max-height: var(--dimension-size-300, 24px);\n opacity: 1;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n max-height var(--_nav-label-fade-motion) 0ms,\n padding var(--_nav-label-fade-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Item ────────────────────────────────────────────────────────────────── */\n\n.panel-nav__item {\n all: unset;\n /* Re-assert after unset — required for `.ds-interaction-fill` stacking. */\n position: relative;\n z-index: 0;\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050, 4px);\n padding: 0 var(--dimension-space-075, 6px);\n height: var(--dimension-size-400, 32px);\n border-radius: var(--ds-control-radius, var(--dimension-radius-025, 2px));\n cursor: default;\n color: var(--_nav-fg-secondary);\n box-sizing: border-box;\n width: 100%;\n user-select: none;\n transition: color var(--effect-motion-short-1, 50ms ease-in-out);\n /* Hover/press: `.ds-interaction-fill` — no --selected (icon/type weight only). */\n}\n\n/* ── Active item ─────────────────────────────────────────────────────────── */\n\n.panel-nav__item--active {\n color: var(--_nav-fg-primary);\n /* Bold typography via ds-text (text-body-medium + emphasis) on the label */\n}\n\n/* ── Item icon ───────────────────────────────────────────────────────────── */\n\n.panel-nav__item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n position: relative;\n z-index: 2;\n}\n\n/* ── Item label ──────────────────────────────────────────────────────────── */\n\n.panel-nav__item-label {\n position: relative;\n z-index: 2;\n flex: 1;\n min-width: 0;\n height: var(--typography-lineheight-md, 20px);\n opacity: 1;\n /* Gradient wipe: mask sweeps left-to-right on expand, right-to-left on collapse */\n /* stylelint-disable-next-line color-named -- mask luminance stops, not theme colors */\n mask-image: linear-gradient(to right, black 50%, transparent 100%);\n /* stylelint-disable-next-line color-named -- mask luminance stops, not theme colors */\n -webkit-mask-image: linear-gradient(to right, black 50%, transparent 100%);\n mask-size: 200% 100%;\n -webkit-mask-size: 200% 100%;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n mask-position var(--_nav-label-fade-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-motion) 0ms;\n}\n\n.panel-nav__item-label-text {\n padding: 0 var(--ds-control-label-inset);\n}\n\n/* ── Item notification dot — 20px suffix zone matches the icon geometry ──── */\n\n.panel-nav__item-dot-box {\n position: absolute;\n z-index: 2;\n right: var(--dimension-space-075, 6px);\n top: var(--dimension-space-075, 6px);\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n pointer-events: none;\n}\n\n.panel-nav__item-dot {\n transition:\n transform var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__item-dot {\n transform: translate(\n calc(var(--dimension-space-075, 6px) + var(--dimension-stroke-width-012, 1px)),\n calc(0px - var(--dimension-space-075, 6px) - var(--dimension-stroke-width-012, 1px))\n );\n}\n\n.panel-nav--animating .panel-nav__item-label {\n text-overflow: clip;\n}\n\n.panel-nav--collapsed .panel-nav__item-label {\n opacity: 0;\n pointer-events: none;\n text-overflow: clip;\n mask-position: 100% 0;\n -webkit-mask-position: 100% 0;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n mask-position var(--_nav-label-fade-out-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-out-motion) 0ms;\n}\n\n.panel-nav--collapsed ds-text.panel-nav__group-label {\n opacity: 0;\n max-height: 0;\n padding: 0 calc(var(--dimension-space-075, 6px) + var(--ds-control-label-inset));\n pointer-events: none;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n max-height var(--_nav-label-fade-out-motion) 0ms,\n padding var(--_nav-label-fade-out-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Footer ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__footer {\n position: relative;\n flex-shrink: 0;\n /* single row: padding*2 + item */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height));\n transition: height var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer {\n /* two rows: padding*2 + item*2 + gap */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height) * 2 + var(--dimension-space-050, 4px));\n}\n\n/* ── Footer icon button (gear / dashboard) ───────────────────────────────── */\n\n.panel-nav__footer-btn {\n position: absolute;\n left: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n --ds-button-unfilled-fg: var(--_nav-fg-secondary);\n --ds-button-unfilled-fg-active: var(--_nav-fg-primary);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Footer user — absolutely positioned, slides from row to below gear ───── */\n\n.panel-nav__footer-user {\n position: absolute;\n /* Expanded row: balanced shell inset with an 8px gear-to-user gap. */\n left: calc(var(--_nav-padding-x) + var(--_nav-footer-btn-w) + var(--dimension-space-100, 8px));\n right: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n width: auto;\n min-width: 0;\n transition:\n left var(--_nav-expand-motion),\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion);\n}\n\n/* Popup-open state keeps the native footer trigger visibly pressed. */\n.panel-nav__footer-user--menu-expanded::after {\n background: var(--_nav-pressed);\n}\n\n.panel-nav__footer-user--menu-expanded {\n --ds-interaction-hover: var(--_nav-pressed);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user {\n /* Compact column keeps the intentionally tighter 4px vertical gap. */\n left: var(--_nav-padding-x);\n top: calc(var(--_nav-padding-y) + var(--_nav-item-height) + var(--dimension-space-050, 4px));\n}\n\n/* Label: absolute so it stays in place and only fades — no mask slide */\n.panel-nav__footer-user-label {\n text-align: right;\n position: absolute;\n left: var(--dimension-space-075, 6px);\n right: calc(var(--dimension-space-075, 6px) + var(--dimension-iconography-md, 20px) + var(--dimension-space-050, 4px));\n top: 50%;\n transform: translateY(-50%);\n flex: none;\n height: var(--typography-lineheight-md, 20px);\n mask-image: none;\n -webkit-mask-image: none;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-label {\n opacity: 0;\n pointer-events: none;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n}\n\n/* ── Footer user icon — cross-fades chevron ↔ circle+initial ────────────── */\n\n/* Absolute — right-anchored in expanded, left-anchored in collapsed to match regular item icon position */\n.panel-nav__footer-user-icon {\n position: absolute;\n left: calc(100% - var(--dimension-iconography-md, 20px) - var(--dimension-space-075, 6px));\n top: 0;\n bottom: 0;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: left var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-icon {\n left: var(--dimension-space-075, 6px);\n}\n\n.panel-nav__footer-icon-expanded,\n.panel-nav__footer-icon-collapsed {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.panel-nav__footer-icon-expanded {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav__footer-icon-collapsed {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-expanded {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-collapsed {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n/* ── User initial — overlaid on the circle icon ─────────────────────────── */\n\n/*\n * Center the caption glyph on the Circle icon. Don't use inset:0 + flex — the\n * inner `.ds-text__element { width: 100% }` stretches and left-aligns the letter.\n * Translate-centering keeps the complete caption metric box on the icon center;\n * the caption variant remains the sole owner of its line-height.\n * (Do not set `inset: auto` after top/left — it resets them.)\n */\nds-text.panel-nav__user-initial,\nds-text.panel-nav__user-initial.ds-text--caption {\n position: absolute;\n top: 50%;\n left: 50%;\n right: auto;\n bottom: auto;\n transform: translate(-50%, -50%);\n display: block;\n width: max-content;\n max-width: none;\n min-width: 0;\n text-align: center;\n color: inherit;\n pointer-events: none;\n}\n\n/* ── Drag-to-resize handle ───────────────────────────────────────────────── */\n\n/* The resize overlay line — extends past the right edge; keep above workspace content */\n.panel-nav__resize-handle {\n position: absolute;\n top: 0;\n /* Centered on the right edge: --dimension-space-050 outside + inside = --dimension-space-100 hit zone */\n right: var(--dimension-space-n050, -4px);\n bottom: 0;\n width: var(--dimension-space-100, 8px);\n z-index: 20;\n cursor: ew-resize;\n touch-action: none;\n /* Needs a real background for Safari hit-testing */\n background: transparent;\n}\n\n/* Safari requires non-transparent fill for hit testing */\n.panel-nav__resize-handle::before {\n content: '';\n position: absolute;\n inset: 0;\n /* stylelint-disable-next-line color-named -- near-invisible hit-test fill; not a theme color */\n background: rgba(0, 0, 0, 0.001);\n}\n\n/* Resize overlay line */\n.panel-nav__resize-handle::after {\n content: '';\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n width: var(--dimension-stroke-width-025, 2px);\n background: var(--color-foreground-primary);\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--effect-motion-short-2, 100ms ease-in-out);\n}\n\n.panel-nav__resize-handle--overlay::after {\n opacity: 1;\n}\n\n/* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .panel-nav,\n .panel-nav *,\n .panel-nav *::before,\n .panel-nav *::after,\n .panel-nav__resize-handle,\n .panel-nav__resize-handle::before,\n .panel-nav__resize-handle::after {\n /* Near-instant — browsers treat 0 as \"no transition\" and can skip the end state. */\n transition-duration: var(--effect-animation-duration-instant) !important;\n animation-duration: var(--effect-animation-duration-instant) !important;\n transition-delay: var(--effect-animation-duration-instant) !important;\n }\n}\n",
|
|
623
|
+
"content": "@import '../../utils/control-density.css';\n@import '../../utils/chrome-layout.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/scrollbar-hidden.css';\n\n/* ── Theme variables ──────────────────────────────────────────────────────── */\n\n.panel-nav {\n /* ─ Sizes ─ */\n --_nav-width: var(--dimension-panel-width-2xs);\n --_nav-width-collapsed: var(--dimension-size-600);\n --_nav-item-height: var(--dimension-size-400, 32px);\n --_nav-padding-x: var(--dimension-space-100, 8px);\n --_nav-padding-y: var(--dimension-space-100, 8px);\n /* gear button outer width = padding*2 + icon */\n --_nav-footer-btn-w: calc(var(--dimension-space-075, 6px) * 2 + var(--dimension-iconography-md, 20px));\n\n /* ─ Motion ─ */\n /* --ds-panel-nav-speed: unitless multiplier; override in Storybook to slow-mo (e.g. 10 = 10×).\n *-dur vars are duration-only (needed for calc); *-motion vars add easing for use in transitions. */\n --_nav-speed: var(--ds-panel-nav-speed, 1);\n --_nav-expand-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-expand-motion: var(--_nav-expand-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-motion: var(--_nav-label-fade-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-out-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-out-motion: var(--_nav-label-fade-out-dur) var(--effect-animation-easing-ease-in-out, ease);\n\n /* ─ Colours – default chrome (app surfaces) ─ */\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Default chrome overrides ────────────────────────────────────────────── */\n\n/* Shell-owned chrome (bg + wash) — panel is a transparent frame. */\n:host-context(ds-shell-app:not([gradient-preset='none'])) .panel-nav,\n:host-context(ds-shell-app.shell-app--paper-texture) .panel-nav {\n background-color: transparent;\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers on these classes later. */\n.panel-nav--dashboard,\n.panel-nav--settings {\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Shell ───────────────────────────────────────────────────────────────── */\n\n.panel-nav {\n display: flex;\n flex-direction: column;\n position: relative;\n width: var(--_nav-width);\n min-width: var(--_nav-width);\n height: 100%;\n background-color: var(--_nav-bg);\n border-right: var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);\n box-sizing: border-box;\n overflow: visible;\n transition:\n width var(--_nav-expand-motion),\n min-width var(--_nav-expand-motion),\n background-color var(--effect-motion-short-3, 200ms ease),\n border-color var(--effect-motion-short-3, 200ms ease);\n font-family: var(--typography-font-family-ui, system-ui);\n isolation: isolate;\n}\n\n.panel-nav__header,\n.panel-nav__body,\n.panel-nav__footer {\n position: relative;\n z-index: 3;\n}\n\n.panel-nav--collapsed {\n width: var(--_nav-width-collapsed);\n min-width: var(--_nav-width-collapsed);\n}\n\n/* ── Header ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__header {\n height: var(--dimension-size-600, 48px);\n flex-shrink: 0;\n border-bottom: var(--dimension-stroke-width-012, 1px) solid var(--_nav-divider);\n transition: border-color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav__header-btn {\n all: unset;\n /* Re-assert after unset — required for `.ds-interaction-fill` stacking. */\n position: relative;\n z-index: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--dimension-space-075, 6px);\n border-radius: var(--ds-control-radius, var(--dimension-radius-025, 2px));\n cursor: default;\n color: var(--_nav-fg-primary);\n flex-shrink: 0;\n box-sizing: border-box;\n transition: color var(--effect-motion-short-3, 200ms ease);\n /* Hover/press: `.ds-interaction-fill` (tokens from --_nav-* via --ds-interaction-*). */\n}\n\n.panel-nav--breakpoint-locked .panel-nav__header-btn {\n cursor: default;\n}\n\n/* Logo — visible by default; instant swap to toggle on hover (no crossfade overlap) */\n.panel-nav__header-logo {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 2;\n opacity: 1;\n}\n\n.panel-nav__m-mark {\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n display: block;\n}\n\n/* Toggle icon — stacked on logo; under hover wash (::after z-index 3) */\n.panel-nav__header-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n inset: 0;\n z-index: 2;\n opacity: 0;\n pointer-events: none;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .panel-nav:not(.panel-nav--breakpoint-locked)\n .panel-nav__header-btn:hover\n .panel-nav__header-logo {\n opacity: 0;\n }\n\n .panel-nav:not(.panel-nav--breakpoint-locked)\n .panel-nav__header-btn:hover\n .panel-nav__header-toggle {\n opacity: 1;\n }\n}\n\n/* ── Scrollable body ─────────────────────────────────────────────────────── */\n\n.panel-nav__body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.panel-nav__sections {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-200, 16px);\n width: 100%;\n}\n\n/* ── Group ───────────────────────────────────────────────────────────────── */\n\n.panel-nav__group {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050, 4px);\n}\n\n/*\n * Section headers — typography via ds-text (text-caption + emphasis).\n * Element selector so display:flex beats Text's inline-block, and padding\n * isn't clobbered by sharing `.panel-nav__item-label-text` on the same host.\n */\nds-text.panel-nav__group-label {\n display: flex;\n align-items: center;\n box-sizing: border-box;\n text-transform: uppercase;\n color: var(--_nav-fg-secondary);\n user-select: none;\n height: var(--dimension-size-300, 24px);\n /* Former nested pad: group-label (075) + item-label-text (025). */\n padding: 0 calc(var(--dimension-space-075, 6px) + var(--ds-control-label-inset));\n white-space: nowrap;\n overflow: hidden;\n max-height: var(--dimension-size-300, 24px);\n opacity: 1;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n max-height var(--_nav-label-fade-motion) 0ms,\n padding var(--_nav-label-fade-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Item ────────────────────────────────────────────────────────────────── */\n\n.panel-nav__item {\n all: unset;\n /* Re-assert after unset — required for `.ds-interaction-fill` stacking. */\n position: relative;\n z-index: 0;\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050, 4px);\n padding: 0 var(--dimension-space-075, 6px);\n height: var(--dimension-size-400, 32px);\n border-radius: var(--ds-control-radius, var(--dimension-radius-025, 2px));\n cursor: default;\n color: var(--_nav-fg-secondary);\n box-sizing: border-box;\n width: 100%;\n user-select: none;\n transition: color var(--effect-motion-short-1, 50ms ease-in-out);\n /* Hover/press: `.ds-interaction-fill` — no --selected (icon/type weight only). */\n}\n\n/* ── Active item ─────────────────────────────────────────────────────────── */\n\n.panel-nav__item--active {\n color: var(--_nav-fg-primary);\n /* Bold typography via ds-text (text-body-medium + emphasis) on the label */\n}\n\n/* ── Item icon ───────────────────────────────────────────────────────────── */\n\n.panel-nav__item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n position: relative;\n z-index: 2;\n}\n\n/* ── Item label ──────────────────────────────────────────────────────────── */\n\n.panel-nav__item-label {\n position: relative;\n z-index: 2;\n flex: 1;\n min-width: 0;\n height: var(--typography-lineheight-md, 20px);\n opacity: 1;\n /* Gradient wipe: mask sweeps left-to-right on expand, right-to-left on collapse */\n /* stylelint-disable-next-line color-named -- mask luminance stops, not theme colors */\n mask-image: linear-gradient(to right, black 50%, transparent 100%);\n /* stylelint-disable-next-line color-named -- mask luminance stops, not theme colors */\n -webkit-mask-image: linear-gradient(to right, black 50%, transparent 100%);\n mask-size: 200% 100%;\n -webkit-mask-size: 200% 100%;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n mask-position var(--_nav-label-fade-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-motion) 0ms;\n}\n\n.panel-nav__item-label-text {\n padding: 0 var(--ds-control-label-inset);\n}\n\n/* ── Item notification dot — 20px suffix zone matches the icon geometry ──── */\n\n.panel-nav__item-dot-box {\n position: absolute;\n z-index: 2;\n right: var(--dimension-space-075, 6px);\n top: var(--dimension-space-075, 6px);\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n pointer-events: none;\n}\n\n.panel-nav__item-dot {\n transition:\n transform var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__item-dot {\n transform: translate(\n calc(var(--dimension-space-075, 6px) + var(--dimension-stroke-width-012, 1px)),\n calc(0px - var(--dimension-space-075, 6px) - var(--dimension-stroke-width-012, 1px))\n );\n}\n\n.panel-nav--animating .panel-nav__item-label {\n text-overflow: clip;\n}\n\n.panel-nav--collapsed .panel-nav__item-label {\n opacity: 0;\n pointer-events: none;\n text-overflow: clip;\n mask-position: 100% 0;\n -webkit-mask-position: 100% 0;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n mask-position var(--_nav-label-fade-out-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-out-motion) 0ms;\n}\n\n.panel-nav--collapsed ds-text.panel-nav__group-label {\n opacity: 0;\n max-height: 0;\n padding: 0 calc(var(--dimension-space-075, 6px) + var(--ds-control-label-inset));\n pointer-events: none;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n max-height var(--_nav-label-fade-out-motion) 0ms,\n padding var(--_nav-label-fade-out-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Footer ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__footer {\n position: relative;\n flex-shrink: 0;\n /* single row: padding*2 + item */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height));\n transition: height var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer {\n /* two rows: padding*2 + item*2 + gap */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height) * 2 + var(--dimension-space-050, 4px));\n}\n\n/* ── Footer icon button (gear / dashboard) ───────────────────────────────── */\n\n.panel-nav__footer-btn {\n position: absolute;\n left: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n --ds-button-unfilled-fg: var(--_nav-fg-secondary);\n --ds-button-unfilled-fg-active: var(--_nav-fg-primary);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Footer user — absolutely positioned, slides from row to below gear ───── */\n\n.panel-nav__footer-user {\n position: absolute;\n /* Expanded row: balanced shell inset with an 8px gear-to-user gap. */\n left: calc(var(--_nav-padding-x) + var(--_nav-footer-btn-w) + var(--dimension-space-100, 8px));\n right: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n width: auto;\n min-width: 0;\n transition:\n left var(--_nav-expand-motion),\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion);\n}\n\n/* Popup-open state keeps the native footer trigger visibly pressed. */\n.panel-nav__footer-user--menu-expanded::after {\n background: var(--_nav-pressed);\n}\n\n.panel-nav__footer-user--menu-expanded {\n --ds-interaction-hover: var(--_nav-pressed);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user {\n /* Compact column keeps the intentionally tighter 4px vertical gap. */\n left: var(--_nav-padding-x);\n top: calc(var(--_nav-padding-y) + var(--_nav-item-height) + var(--dimension-space-050, 4px));\n}\n\n/* Label: absolute so it stays in place and only fades — no mask slide */\n.panel-nav__footer-user-label {\n text-align: right;\n position: absolute;\n left: var(--dimension-space-075, 6px);\n right: calc(var(--dimension-space-075, 6px) + var(--dimension-iconography-md, 20px) + var(--dimension-space-050, 4px));\n top: 50%;\n transform: translateY(-50%);\n flex: none;\n height: var(--typography-lineheight-md, 20px);\n mask-image: none;\n -webkit-mask-image: none;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-label {\n opacity: 0;\n pointer-events: none;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n}\n\n/* ── Footer user icon — cross-fades chevron ↔ circle+initial ────────────── */\n\n/* Absolute — right-anchored in expanded, left-anchored in collapsed to match regular item icon position */\n.panel-nav__footer-user-icon {\n position: absolute;\n left: calc(100% - var(--dimension-iconography-md, 20px) - var(--dimension-space-075, 6px));\n top: 0;\n bottom: 0;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: left var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-icon {\n left: var(--dimension-space-075, 6px);\n}\n\n.panel-nav__footer-icon-expanded,\n.panel-nav__footer-icon-collapsed {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.panel-nav__footer-icon-expanded {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav__footer-icon-collapsed {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-expanded {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-collapsed {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n/* ── User initial — overlaid on the circle icon ─────────────────────────── */\n\n/*\n * Center the caption glyph on the Circle icon. Don't use inset:0 + flex — the\n * inner `.ds-text__element { width: 100% }` stretches and left-aligns the letter.\n * Translate-centering keeps the complete caption metric box on the icon center;\n * the caption variant remains the sole owner of its line-height.\n * (Do not set `inset: auto` after top/left — it resets them.)\n */\nds-text.panel-nav__user-initial,\nds-text.panel-nav__user-initial.ds-text--caption {\n position: absolute;\n top: 50%;\n left: 50%;\n right: auto;\n bottom: auto;\n transform: translate(-50%, -50%);\n display: block;\n width: max-content;\n max-width: none;\n min-width: 0;\n text-align: center;\n color: inherit;\n pointer-events: none;\n}\n\n/* ── Drag-to-resize handle ───────────────────────────────────────────────── */\n\n/* The resize overlay line — extends past the right edge; keep above workspace content */\n.panel-nav__resize-handle {\n position: absolute;\n top: 0;\n /* Centered on the right edge: --dimension-space-050 outside + inside = --dimension-space-100 hit zone */\n right: var(--dimension-space-n050, -4px);\n bottom: 0;\n width: var(--dimension-space-100, 8px);\n z-index: 20;\n cursor: ew-resize;\n touch-action: none;\n /* Needs a real background for Safari hit-testing */\n background: transparent;\n}\n\n/* Safari requires non-transparent fill for hit testing */\n.panel-nav__resize-handle::before {\n content: '';\n position: absolute;\n inset: 0;\n /* stylelint-disable-next-line color-named -- near-invisible hit-test fill; not a theme color */\n background: rgba(0, 0, 0, 0.001);\n}\n\n/* Resize overlay line */\n.panel-nav__resize-handle::after {\n content: '';\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n width: var(--dimension-stroke-width-025, 2px);\n background: var(--color-foreground-primary);\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--effect-motion-short-2, 100ms ease-in-out);\n}\n\n.panel-nav__resize-handle--overlay::after {\n opacity: 1;\n}\n\n/* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .panel-nav,\n .panel-nav *,\n .panel-nav *::before,\n .panel-nav *::after,\n .panel-nav__resize-handle,\n .panel-nav__resize-handle::before,\n .panel-nav__resize-handle::after {\n /* Near-instant — browsers treat 0 as \"no transition\" and can skip the end state. */\n transition-duration: var(--effect-animation-duration-instant) !important;\n animation-duration: var(--effect-animation-duration-instant) !important;\n transition-delay: var(--effect-animation-duration-instant) !important;\n }\n}\n",
|
|
624
624
|
"type": "registry:ui"
|
|
625
625
|
},
|
|
626
626
|
{
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "paper-texture",
|
|
4
|
+
"title": "PaperTexture",
|
|
5
|
+
"description": "A decorative Paper Design WebGL2 paper texture surface with Storybook-tunable literal shader parameters and a static default frame.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/paper-texture",
|
|
11
|
+
"source": "src/wc/components/PaperTexture/PaperTexture.tsx",
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "specialized",
|
|
15
|
+
"status": "experimental",
|
|
16
|
+
"summary": "A decorative Paper Design WebGL2 paper texture surface with Storybook-tunable literal shader parameters and a static default frame.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"A shell or other visual surface needs a paper or cardboard texture layered above an existing background.",
|
|
19
|
+
"Designers need to tune paper texture parameters interactively before choosing a baked static asset."
|
|
20
|
+
],
|
|
21
|
+
"avoidWhen": [
|
|
22
|
+
"The surface needs semantic content, interaction, or accessible information.",
|
|
23
|
+
"The target environment cannot provide WebGL2 and no pre-baked fallback asset is available.",
|
|
24
|
+
"A token-driven color treatment is required; this component accepts literal shader colors only."
|
|
25
|
+
],
|
|
26
|
+
"alternatives": [],
|
|
27
|
+
"commonlyComposedWith": [
|
|
28
|
+
"component:ds-shell-app"
|
|
29
|
+
],
|
|
30
|
+
"accessibility": [
|
|
31
|
+
"The texture is decorative and is hidden from assistive technology.",
|
|
32
|
+
"Keep all meaningful content in the owning surface above or beside the texture."
|
|
33
|
+
],
|
|
34
|
+
"states": [
|
|
35
|
+
"The default speed is zero, so a texture renders one deterministic frame without a recurring animation loop.",
|
|
36
|
+
"Paper shader colors are literal CSS color strings and are not resolved from TokoMo tokens.",
|
|
37
|
+
"If WebGL2 or the noise texture is unavailable, the decorative layer removes itself without blocking the owning surface."
|
|
38
|
+
],
|
|
39
|
+
"responsiveBehavior": [
|
|
40
|
+
"The shader observes its host dimensions and follows the owning surface through resize changes.",
|
|
41
|
+
"minPixelRatio and maxPixelCount are available for explicit quality/performance tuning."
|
|
42
|
+
],
|
|
43
|
+
"frameworkCaveats": {
|
|
44
|
+
"customElements": [
|
|
45
|
+
"Assign config as a JavaScript property; nested shader values are not intended for serialized HTML attributes."
|
|
46
|
+
],
|
|
47
|
+
"react": [
|
|
48
|
+
"Use the generated wrapper and pass the config object as a property."
|
|
49
|
+
],
|
|
50
|
+
"angular": [
|
|
51
|
+
"Bind the config object as a property so nested shader values are preserved."
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"references": [
|
|
55
|
+
{
|
|
56
|
+
"label": "Storybook parameter playground",
|
|
57
|
+
"path": "src/wc/components/PaperTexture/PaperTexture.stories.ts"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"api": {
|
|
62
|
+
"props": {
|
|
63
|
+
"config": {
|
|
64
|
+
"type": "PaperTextureConfig",
|
|
65
|
+
"resolvedType": "PaperTextureConfig",
|
|
66
|
+
"default": "{}",
|
|
67
|
+
"required": false,
|
|
68
|
+
"mutable": false,
|
|
69
|
+
"description": "Paper Design shader parameters. Set as a JavaScript property."
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"events": [],
|
|
73
|
+
"methods": [],
|
|
74
|
+
"slots": []
|
|
75
|
+
},
|
|
76
|
+
"props": {
|
|
77
|
+
"config": {
|
|
78
|
+
"type": "PaperTextureConfig",
|
|
79
|
+
"resolvedType": "PaperTextureConfig",
|
|
80
|
+
"default": "{}",
|
|
81
|
+
"required": false,
|
|
82
|
+
"mutable": false,
|
|
83
|
+
"description": "Paper Design shader parameters. Set as a JavaScript property."
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"events": [],
|
|
87
|
+
"methods": [],
|
|
88
|
+
"slots": [],
|
|
89
|
+
"exports": {
|
|
90
|
+
"customElement": "ds-paper-texture",
|
|
91
|
+
"react": "DsPaperTexture",
|
|
92
|
+
"angular": "DsPaperTexture"
|
|
93
|
+
},
|
|
94
|
+
"consumption": {
|
|
95
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
96
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
97
|
+
"customElements": {
|
|
98
|
+
"import": "import '@ds-mo/ui/dist/components/ds-paper-texture.js';",
|
|
99
|
+
"example": "<ds-paper-texture></ds-paper-texture>"
|
|
100
|
+
},
|
|
101
|
+
"react": {
|
|
102
|
+
"import": "import { DsPaperTexture } from '@ds-mo/ui/react';",
|
|
103
|
+
"example": "<DsPaperTexture />"
|
|
104
|
+
},
|
|
105
|
+
"angular": {
|
|
106
|
+
"import": "import { DsPaperTexture } from '@ds-mo/ui/angular/ds-paper-texture';",
|
|
107
|
+
"example": "<ds-paper-texture></ds-paper-texture>"
|
|
108
|
+
},
|
|
109
|
+
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: config.",
|
|
110
|
+
"peerDependencies": {
|
|
111
|
+
"required": [
|
|
112
|
+
"@ds-mo/tokens ^6.5.0",
|
|
113
|
+
"@ds-mo/icons ^7.0.0"
|
|
114
|
+
],
|
|
115
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"dependencies": [
|
|
120
|
+
"@ds-mo/ui",
|
|
121
|
+
"@ds-mo/tokens"
|
|
122
|
+
],
|
|
123
|
+
"registryDependencies": [],
|
|
124
|
+
"files": [
|
|
125
|
+
{
|
|
126
|
+
"path": "src/wc/components/PaperTexture/PaperTexture.css",
|
|
127
|
+
"content": ":host {\n position: absolute;\n inset: 0;\n display: block;\n overflow: hidden;\n pointer-events: none;\n opacity: var(--ds-paper-texture-opacity, 1);\n}\n\n.paper-texture__mount {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n overflow: hidden;\n isolation: isolate;\n}\n\n.paper-texture__mount[data-paper-shader] canvas {\n z-index: 0;\n}\n\n:host([data-paper-texture-error]) {\n display: none;\n}\n",
|
|
128
|
+
"type": "registry:ui"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"path": "src/wc/components/PaperTexture/PaperTexture.tsx",
|
|
132
|
+
"content": "import { Component, Element, Host, Prop, Watch, h } from '@stencil/core';\nimport {\n emptyPixel,\n getShaderColorFromString,\n getShaderNoiseTexture,\n paperTextureFragmentShader,\n ShaderFitOptions,\n ShaderMount,\n type ShaderMountUniforms,\n} from '@paper-design/shaders';\nimport { DEFAULT_PAPER_TEXTURE_CONFIG, type PaperTextureConfig } from './paper-texture-types';\n\nlet emptyPixelPromise: Promise<HTMLImageElement> | undefined;\nlet noiseTexturePromise: Promise<HTMLImageElement> | undefined;\n\nfunction waitForImage(image: HTMLImageElement): Promise<HTMLImageElement> {\n if (image.complete && image.naturalWidth > 0) return Promise.resolve(image);\n\n return new Promise((resolve, reject) => {\n const onLoad = () => {\n cleanup();\n resolve(image);\n };\n const onError = () => {\n cleanup();\n reject(new Error(`Paper texture image failed to load: ${image.src}`));\n };\n const cleanup = () => {\n image.removeEventListener('load', onLoad);\n image.removeEventListener('error', onError);\n };\n\n image.addEventListener('load', onLoad, { once: true });\n image.addEventListener('error', onError, { once: true });\n });\n}\n\nfunction loadImage(source: HTMLImageElement | string | undefined): Promise<HTMLImageElement> {\n if (source instanceof HTMLImageElement) return waitForImage(source);\n\n const image = new Image();\n if (source && source !== emptyPixel) image.crossOrigin = 'anonymous';\n image.src = source || emptyPixel;\n return waitForImage(image);\n}\n\nfunction loadEmptyPixel(): Promise<HTMLImageElement> {\n emptyPixelPromise ??= loadImage(emptyPixel);\n return emptyPixelPromise;\n}\n\nfunction loadNoiseTexture(): Promise<HTMLImageElement> {\n noiseTexturePromise ??= Promise.resolve(getShaderNoiseTexture()).then(image => {\n if (!image) throw new Error('Paper texture noise texture is unavailable');\n return waitForImage(image);\n });\n return noiseTexturePromise;\n}\n\n@Component({\n tag: 'ds-paper-texture',\n styleUrl: 'PaperTexture.css',\n scoped: true,\n})\nexport class PaperTexture {\n @Element() el!: HTMLElement;\n\n /** Paper Design shader parameters. Set as a JavaScript property. */\n @Prop() config: PaperTextureConfig = {};\n\n private shaderMount?: ShaderMount;\n private syncGeneration = 0;\n private hasLoaded = false;\n\n componentDidLoad() {\n this.hasLoaded = true;\n void this.syncShader();\n }\n\n disconnectedCallback() {\n this.syncGeneration += 1;\n this.disposeShader();\n }\n\n @Watch('config')\n onConfigChange() {\n if (this.hasLoaded) void this.syncShader();\n }\n\n private resolvedConfig(): Required<Omit<PaperTextureConfig, 'image'>> &\n Pick<PaperTextureConfig, 'image'> {\n return {\n ...DEFAULT_PAPER_TEXTURE_CONFIG,\n ...(this.config ?? {}),\n };\n }\n\n private async syncShader() {\n const generation = ++this.syncGeneration;\n const config = this.resolvedConfig();\n\n try {\n const [image, noiseTexture] = await Promise.all([\n loadImage(config.image).catch(() => loadEmptyPixel()),\n loadNoiseTexture(),\n ]);\n\n if (generation !== this.syncGeneration || !this.el.isConnected) return;\n\n const uniforms = this.buildUniforms(config, image, noiseTexture);\n const mountElement = this.el.querySelector<HTMLElement>('.paper-texture__mount');\n if (!mountElement) return;\n\n if (!this.shaderMount) {\n this.shaderMount = new ShaderMount(\n mountElement,\n paperTextureFragmentShader,\n uniforms,\n undefined,\n config.speed,\n config.frame,\n config.minPixelRatio,\n config.maxPixelCount,\n ['u_image']\n );\n } else {\n this.shaderMount.setUniforms(uniforms);\n this.shaderMount.setSpeed(config.speed);\n this.shaderMount.setFrame(config.frame);\n this.shaderMount.setMinPixelRatio(config.minPixelRatio);\n this.shaderMount.setMaxPixelCount(config.maxPixelCount);\n }\n\n this.el.removeAttribute('data-paper-texture-error');\n } catch (error) {\n if (generation !== this.syncGeneration) return;\n this.disposeShader();\n this.el.querySelector<HTMLElement>('.paper-texture__mount')?.replaceChildren();\n this.el.setAttribute('data-paper-texture-error', 'unavailable');\n console.warn('[ds-paper-texture] WebGL2 paper texture unavailable.', error);\n }\n }\n\n private buildUniforms(\n config: Required<Omit<PaperTextureConfig, 'image'>> & Pick<PaperTextureConfig, 'image'>,\n image: HTMLImageElement,\n noiseTexture: HTMLImageElement\n ): ShaderMountUniforms {\n return {\n u_image: image,\n u_noiseTexture: noiseTexture,\n u_colorFront: getShaderColorFromString(config.colorFront),\n u_colorBack: getShaderColorFromString(config.colorBack),\n u_contrast: config.contrast,\n u_roughness: config.roughness,\n u_fiber: config.fiber,\n u_fiberSize: config.fiberSize,\n u_crumples: config.crumples,\n u_crumpleSize: config.crumpleSize,\n u_foldCount: config.foldCount,\n u_folds: config.folds,\n u_fade: config.fade,\n u_drops: config.drops,\n u_seed: config.seed,\n u_fit: ShaderFitOptions[config.fit],\n u_scale: config.scale,\n u_rotation: config.rotation,\n u_offsetX: config.offsetX,\n u_offsetY: config.offsetY,\n u_originX: config.originX,\n u_originY: config.originY,\n u_worldWidth: config.worldWidth,\n u_worldHeight: config.worldHeight,\n };\n }\n\n private disposeShader() {\n this.shaderMount?.dispose();\n this.shaderMount = undefined;\n }\n\n render() {\n const config = this.resolvedConfig();\n return (\n <Host aria-hidden=\"true\" style={{ '--ds-paper-texture-opacity': String(config.opacity) }}>\n <div class=\"paper-texture__mount\" />\n </Host>\n );\n }\n}\n",
|
|
133
|
+
"type": "registry:ui"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"path": "src/wc/components/PaperTexture/paper-texture-types.ts",
|
|
137
|
+
"content": "export type PaperTextureFit = 'none' | 'contain' | 'cover';\n\n/**\n * Literal parameters forwarded to Paper Design's paper texture shader.\n * Colors intentionally remain ordinary CSS color strings; they are not token\n * references and are parsed once into shader RGBA uniforms.\n */\nexport interface PaperTextureConfig {\n image?: HTMLImageElement | string;\n colorFront?: string;\n colorBack?: string;\n contrast?: number;\n roughness?: number;\n fiber?: number;\n fiberSize?: number;\n crumples?: number;\n crumpleSize?: number;\n folds?: number;\n foldCount?: number;\n fade?: number;\n drops?: number;\n seed?: number;\n fit?: PaperTextureFit;\n scale?: number;\n rotation?: number;\n originX?: number;\n originY?: number;\n offsetX?: number;\n offsetY?: number;\n worldWidth?: number;\n worldHeight?: number;\n speed?: number;\n frame?: number;\n minPixelRatio?: number;\n maxPixelCount?: number;\n /** CSS opacity applied after the shader has rendered. */\n opacity?: number;\n}\n\nexport const DEFAULT_PAPER_TEXTURE_CONFIG: Required<Omit<PaperTextureConfig, 'image'>> = {\n colorFront: '#b8b8b8',\n colorBack: '#ffffff',\n contrast: 0.3,\n roughness: 0.4,\n fiber: 0.3,\n fiberSize: 0.2,\n crumples: 0.3,\n crumpleSize: 0.35,\n folds: 0.65,\n foldCount: 5,\n fade: 0,\n drops: 0.2,\n seed: 5.8,\n fit: 'cover',\n scale: 0.6,\n rotation: 0,\n originX: 0.5,\n originY: 0.5,\n offsetX: 0,\n offsetY: 0,\n worldWidth: 0,\n worldHeight: 0,\n speed: 0,\n frame: 0,\n minPixelRatio: 2,\n maxPixelCount: 1920 * 1080 * 4,\n opacity: 1,\n};\n",
|
|
138
|
+
"type": "registry:ui"
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
@@ -6928,6 +6928,85 @@
|
|
|
6928
6928
|
"tooltip"
|
|
6929
6929
|
]
|
|
6930
6930
|
},
|
|
6931
|
+
{
|
|
6932
|
+
"name": "paper-texture",
|
|
6933
|
+
"title": "PaperTexture",
|
|
6934
|
+
"description": "A decorative Paper Design WebGL2 paper texture surface with Storybook-tunable literal shader parameters and a static default frame.",
|
|
6935
|
+
"type": "registry:ui",
|
|
6936
|
+
"meta": {
|
|
6937
|
+
"library": "@ds-mo/ui",
|
|
6938
|
+
"distribution": "npm",
|
|
6939
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/paper-texture",
|
|
6940
|
+
"source": "src/wc/components/PaperTexture/PaperTexture.tsx",
|
|
6941
|
+
"intentStatus": "complete",
|
|
6942
|
+
"intent": {
|
|
6943
|
+
"audience": "specialized",
|
|
6944
|
+
"status": "experimental",
|
|
6945
|
+
"summary": "A decorative Paper Design WebGL2 paper texture surface with Storybook-tunable literal shader parameters and a static default frame.",
|
|
6946
|
+
"useWhen": [
|
|
6947
|
+
"A shell or other visual surface needs a paper or cardboard texture layered above an existing background.",
|
|
6948
|
+
"Designers need to tune paper texture parameters interactively before choosing a baked static asset."
|
|
6949
|
+
],
|
|
6950
|
+
"avoidWhen": [
|
|
6951
|
+
"The surface needs semantic content, interaction, or accessible information.",
|
|
6952
|
+
"The target environment cannot provide WebGL2 and no pre-baked fallback asset is available.",
|
|
6953
|
+
"A token-driven color treatment is required; this component accepts literal shader colors only."
|
|
6954
|
+
],
|
|
6955
|
+
"alternatives": [],
|
|
6956
|
+
"commonlyComposedWith": [
|
|
6957
|
+
"component:ds-shell-app"
|
|
6958
|
+
],
|
|
6959
|
+
"accessibility": [
|
|
6960
|
+
"The texture is decorative and is hidden from assistive technology.",
|
|
6961
|
+
"Keep all meaningful content in the owning surface above or beside the texture."
|
|
6962
|
+
],
|
|
6963
|
+
"states": [
|
|
6964
|
+
"The default speed is zero, so a texture renders one deterministic frame without a recurring animation loop.",
|
|
6965
|
+
"Paper shader colors are literal CSS color strings and are not resolved from TokoMo tokens.",
|
|
6966
|
+
"If WebGL2 or the noise texture is unavailable, the decorative layer removes itself without blocking the owning surface."
|
|
6967
|
+
],
|
|
6968
|
+
"responsiveBehavior": [
|
|
6969
|
+
"The shader observes its host dimensions and follows the owning surface through resize changes.",
|
|
6970
|
+
"minPixelRatio and maxPixelCount are available for explicit quality/performance tuning."
|
|
6971
|
+
],
|
|
6972
|
+
"frameworkCaveats": {
|
|
6973
|
+
"customElements": [
|
|
6974
|
+
"Assign config as a JavaScript property; nested shader values are not intended for serialized HTML attributes."
|
|
6975
|
+
],
|
|
6976
|
+
"react": [
|
|
6977
|
+
"Use the generated wrapper and pass the config object as a property."
|
|
6978
|
+
],
|
|
6979
|
+
"angular": [
|
|
6980
|
+
"Bind the config object as a property so nested shader values are preserved."
|
|
6981
|
+
]
|
|
6982
|
+
},
|
|
6983
|
+
"references": [
|
|
6984
|
+
{
|
|
6985
|
+
"label": "Storybook parameter playground",
|
|
6986
|
+
"path": "src/wc/components/PaperTexture/PaperTexture.stories.ts"
|
|
6987
|
+
}
|
|
6988
|
+
]
|
|
6989
|
+
},
|
|
6990
|
+
"props": {
|
|
6991
|
+
"config": {
|
|
6992
|
+
"type": "PaperTextureConfig",
|
|
6993
|
+
"required": false
|
|
6994
|
+
}
|
|
6995
|
+
},
|
|
6996
|
+
"events": [],
|
|
6997
|
+
"slots": [],
|
|
6998
|
+
"exports": {
|
|
6999
|
+
"customElement": "ds-paper-texture",
|
|
7000
|
+
"react": "DsPaperTexture",
|
|
7001
|
+
"angular": "DsPaperTexture"
|
|
7002
|
+
}
|
|
7003
|
+
},
|
|
7004
|
+
"dependencies": [
|
|
7005
|
+
"@ds-mo/ui",
|
|
7006
|
+
"@ds-mo/tokens"
|
|
7007
|
+
],
|
|
7008
|
+
"registryDependencies": []
|
|
7009
|
+
},
|
|
6931
7010
|
{
|
|
6932
7011
|
"name": "radio",
|
|
6933
7012
|
"title": "Radio",
|
|
@@ -7515,7 +7594,8 @@
|
|
|
7515
7594
|
"component:ds-mobile-bar-nav",
|
|
7516
7595
|
"component:ds-shell-page",
|
|
7517
7596
|
"component:ds-banner",
|
|
7518
|
-
"component:ds-swatch-picker"
|
|
7597
|
+
"component:ds-swatch-picker",
|
|
7598
|
+
"component:ds-paper-texture"
|
|
7519
7599
|
],
|
|
7520
7600
|
"patterns": [
|
|
7521
7601
|
"pattern:application-shell",
|
|
@@ -7541,6 +7621,7 @@
|
|
|
7541
7621
|
"Use composition=slotted only for advanced application architectures that intentionally own all lower-level chrome wiring.",
|
|
7542
7622
|
"Set gradientPreset to the single resolved global chrome preference: neutral by default, none for solid chrome, or cool, neutral, warm, and fresh for token-based washes.",
|
|
7543
7623
|
"The application owns gradient preference persistence, cross-tab synchronization, and server or account precedence; ShellApp owns rendering the resolved preset across authenticated routes.",
|
|
7624
|
+
"The optional paperTexture property adds a decorative Paper Design texture layer above the gradient or solid chrome frame; its color values are literal shader inputs rather than design tokens.",
|
|
7544
7625
|
"Dashboard represents day-to-day operational work while Settings represents administrative and configuration work; route state selects the context.",
|
|
7545
7626
|
"Replace navigation and pageChrome together from one completed route state.",
|
|
7546
7627
|
"When pageChrome.showBack marks a detail depth, mobile chrome keeps the page heading as its identity and presents only local subsections; peer route tabs remain in desktop BarNav rather than replacing the detail heading.",
|
|
@@ -7561,20 +7642,24 @@
|
|
|
7561
7642
|
"A slotted Banner remains below the top safe-area inset and above routed, navigation, fullscreen-tool, and mobile global-surface presentations.",
|
|
7562
7643
|
"Mobile global surfaces conceal and inert the routed workspace without unmounting it, preserving nested routes and scroll state.",
|
|
7563
7644
|
"Keep PanelNav width, BarNav height, PanelTools chrome, and main-content origin stable except during intentional responsive or collapse transitions.",
|
|
7645
|
+
"Paper texture chrome is desktop/tablet-only and follows the fixed viewport phase of the shell chrome; mobile remains a solid primary stage.",
|
|
7564
7646
|
"Managed page-header capacity follows only the fixed shell breakpoint: roomy on desktop and compact on tablet. Opening PanelTools does not compact or constrain BarTitle.",
|
|
7565
7647
|
"The bottom bar remains visible at all route and tool depths in the foundation release."
|
|
7566
7648
|
],
|
|
7567
7649
|
"frameworkCaveats": {
|
|
7568
7650
|
"customElements": [
|
|
7569
7651
|
"Assign navigation, pageChrome, and tools as JavaScript properties on the managed ShellApp.",
|
|
7652
|
+
"Assign paperTexture as a JavaScript property when enabling the optional decorative layer.",
|
|
7570
7653
|
"Stamp route-derived nav-style before components connect when deterministic first paint is required."
|
|
7571
7654
|
],
|
|
7572
7655
|
"react": [
|
|
7573
7656
|
"Use the generated ShellApp wrapper, structured object props, named tool-view slots, and stable routed children.",
|
|
7657
|
+
"Pass paperTexture as a structured property when the shell needs the optional texture layer.",
|
|
7574
7658
|
"Resolve all shell inputs from one router state before rendering the composition."
|
|
7575
7659
|
],
|
|
7576
7660
|
"angular": [
|
|
7577
7661
|
"Use the generated standalone ShellApp adapter, bind structured object props, and preserve named tool-view slots.",
|
|
7662
|
+
"Bind paperTexture as a property so nested shader parameters are preserved.",
|
|
7578
7663
|
"Bind complex inputs as properties and update navigation inputs atomically from one resolved router state.",
|
|
7579
7664
|
"Bind both nav-style attributes and properties when deterministic first paint is required."
|
|
7580
7665
|
]
|
|
@@ -7627,6 +7712,10 @@
|
|
|
7627
7712
|
"type": "ShellPageChromeConfig",
|
|
7628
7713
|
"required": false
|
|
7629
7714
|
},
|
|
7715
|
+
"paperTexture": {
|
|
7716
|
+
"type": "PaperTextureConfig",
|
|
7717
|
+
"required": false
|
|
7718
|
+
},
|
|
7630
7719
|
"shortcutsEnabled": {
|
|
7631
7720
|
"type": "boolean",
|
|
7632
7721
|
"required": false
|
|
@@ -7713,6 +7802,7 @@
|
|
|
7713
7802
|
"mobile-header",
|
|
7714
7803
|
"mobile-sheet-nav",
|
|
7715
7804
|
"panel-nav",
|
|
7805
|
+
"paper-texture",
|
|
7716
7806
|
"shell-page",
|
|
7717
7807
|
"shell-tools",
|
|
7718
7808
|
"tooltip"
|