mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Merge pull request #524 from PR0M3TH3AN/codex/introduce-named-tokens-for-design-system
Centralize floating metrics tokens and add lint
This commit is contained in:
12 files changed
+498
-24
No files matched your search
@@ -1,3 +1,3 @@
|
||||
:root{--color-page:#fff;--color-page-alt:#f9fafb;--color-surface:#0f172a;--color-surface-alt:#111827;--color-bg:var(--color-surface);--color-card:#1e293b;--color-card-private:#3b1d74;--color-card-critical:#3f0b13;--color-panel:#1f2937;--color-panel-hover:#334155;--color-border:#374151;--color-border-subtle:#d1d5db;--color-primary:#fe0032;--color-secondary:#ff93a5;--color-logo-accent:var(--color-primary);--color-logo-wordmark:var(--color-neutral-700);--color-logo-wordmark-inverse:var(--color-white);--color-logo-background:var(--color-neutral-700);--color-logo-background-inverse:var(--color-white);--color-info:#60a5fa;--color-info-strong:#2563eb;--color-info-pressed:#1d4ed8;--color-info-glass:rgba(37,99,235,.18);--color-muted-strong:#6b7280;--color-text:#f8fafc;--color-text-strong:#0f172a;--color-text-muted:var(--color-muted);--color-muted:#94a3b8;--color-muted-alt:#9ca3af;--color-subtle-text:#e2e8f0;--color-subtle:#e5e7eb;--color-critical:#7f1d1d;--color-critical-strong:#991b1b;--color-warning:#facc15;--color-warning-strong:#d97706;--color-warning-surface:rgba(146,64,14,.24);--color-warning-glass:rgba(250,204,21,.18);--color-neutral-700:#3f3f46;--color-neutral-600:#52525b;--color-white:#fff;--color-black:#000;--color-overlay:#1a2234;--color-overlay-black:rgba(0,0,0,.9);--color-overlay-muted:rgba(15,23,42,.55);--color-overlay-strong:rgba(15,23,42,.75);--color-overlay-darker:rgba(15,23,42,.88);--color-overlay-panel:rgba(26,34,52,.82);--color-overlay-panel-soft:rgba(26,34,52,.65);--color-overlay-panel-tint:rgba(31,41,55,.5);--color-overlay-panel-glass:rgba(28,37,58,.72);--color-overlay-panel-glass-soft:rgba(31,41,55,.55);--color-modal-backdrop:rgba(8,12,24,.58);--color-modal-surface:rgba(20,28,46,.78);--color-overlay-veil:rgba(11,17,32,.96);--color-overlay-banner:rgba(51,65,85,.95);--color-sidebar-surface:rgba(17,24,39,.94);--color-sidebar-surface-alt:rgba(11,22,48,.78);--color-sidebar-border:rgba(148,163,184,.28);--color-sidebar-border-strong:rgba(148,163,184,.42);--color-sidebar-hover:rgba(56,189,248,.18);--color-sidebar-hover-strong:rgba(129,140,248,.28);--color-sidebar-glow:rgba(56,189,248,.35);--color-sidebar-accent-start:rgba(56,189,248,.9);--color-sidebar-accent-end:rgba(129,140,248,.85);--color-sidebar-text-muted:rgba(203,213,225,.75);--color-overlay-panel-gradient-start:rgba(30,41,59,.92);--color-overlay-panel-gradient-end:rgba(15,23,42,.92);--color-surface-tint-subtle:rgba(148,163,184,.08);--color-border-translucent:rgba(148,163,184,.18);--color-border-translucent-strong:rgba(148,163,184,.25);--color-border-translucent-bright:rgba(148,163,184,.32);--color-border-translucent-stronger:rgba(148,163,184,.35);--color-border-translucent-strongest:rgba(148,163,184,.4);--color-border-translucent-glow:rgba(148,163,184,.55);--color-border-translucent-medium:rgba(148,163,184,.2);--color-border-neutral-soft:rgba(229,231,235,.35);--color-border-info-soft:rgba(96,165,250,.35);--color-border-sidebar-strong:rgba(55,65,81,.9);--color-ring-private:rgba(168,85,247,.55);--color-ring-private-hover:rgba(192,132,252,.65);--color-ring-critical:rgba(239,68,68,.4);--color-ring-critical-soft:rgba(239,68,68,.35);--color-ring-critical-hover:rgba(239,68,68,.45);--color-ring-critical-strong:rgba(239,68,68,.55);--color-ring-alert:rgba(220,38,38,.6);--color-gradient-visibility-start:rgba(129,140,248,.4);--color-gradient-visibility-end:rgba(192,132,252,.32);--color-white-soft:hsla(0,0%,100%,.1);--color-white-softer:hsla(0,0%,100%,.3);--color-white-softest:hsla(0,0%,100%,.4);--color-shadow-floating:rgba(8,11,30,.55);--color-shadow-elevated:rgba(8,11,30,.45);--color-shadow-panel:rgba(15,23,42,.35);--color-shadow-notification:rgba(15,23,42,.3);--color-shadow-intense:rgba(250,204,21,.48);--color-shadow-sidebar:rgba(0,0,0,.45);--color-divider-strong:rgba(15,23,42,.35);--color-text-frosted-strong:rgba(226,232,240,.94);--color-text-frosted:rgba(226,232,240,.9);--color-text-frosted-soft:rgba(226,232,240,.82);--scrollbar-width:0.5rem;--scrollbar-track:var(--color-white-soft);--scrollbar-thumb:var(--color-white-softer);--scrollbar-thumb-hover:var(--color-white-softest);--z-sidebar-overlay:10;--z-sidebar:20;--z-modal-root:100;--z-modal-overlay:110;--z-modal-content:120;--z-modal-player:130;--sidebar-width-expanded:16rem;--sidebar-width-collapsed:4rem;--sidebar-width:var(--sidebar-width-expanded);--sidebar-control-size:2.75rem;--sidebar-content-gap:1rem;--space-sidebar-shell-inline:var(--space-lg);--space-sidebar-shell-block-start:var(--space-lg);--space-sidebar-shell-block-end:1.75rem;--space-sidebar-scroll-gap:1.75rem;--space-sidebar-panel-gap:1.35rem;--space-sidebar-panel-padding-block:1.35rem;--space-sidebar-panel-padding-inline:1.1rem;--space-sidebar-footer-gap:0.85rem;--space-sidebar-trigger-padding-block:0.78rem;--space-sidebar-popover-offset:0.85rem;--space-sidebar-popover-padding-block:0.85rem;--space-sidebar-popover-padding-inline:0.65rem;--space-sidebar-popover-gap:0.45rem;--space-sidebar-nav-padding-block:0.7rem;--space-sidebar-subnaLine truncated
|
||||
:root{--color-page:#fff;--color-page-alt:#f9fafb;--color-surface:#0f172a;--color-surface-alt:#111827;--color-bg:var(--color-surface);--color-card:#1e293b;--color-card-private:#3b1d74;--color-card-critical:#3f0b13;--color-panel:#1f2937;--color-panel-hover:#334155;--color-border:#374151;--color-border-subtle:#d1d5db;--color-primary:#fe0032;--color-secondary:#ff93a5;--color-logo-accent:var(--color-primary);--color-logo-wordmark:var(--color-neutral-700);--color-logo-wordmark-inverse:var(--color-white);--color-logo-background:var(--color-neutral-700);--color-logo-background-inverse:var(--color-white);--color-info:#60a5fa;--color-info-strong:#2563eb;--color-info-pressed:#1d4ed8;--color-info-glass:rgba(37,99,235,.18);--color-muted-strong:#6b7280;--color-text:#f8fafc;--color-text-strong:#0f172a;--color-text-muted:var(--color-muted);--color-muted:#94a3b8;--color-muted-alt:#9ca3af;--color-subtle-text:#e2e8f0;--color-subtle:#e5e7eb;--color-critical:#7f1d1d;--color-critical-strong:#991b1b;--color-warning:#facc15;--color-warning-strong:#d97706;--color-warning-surface:rgba(146,64,14,.24);--color-warning-glass:rgba(250,204,21,.18);--color-neutral-700:#3f3f46;--color-neutral-600:#52525b;--color-white:#fff;--color-black:#000;--color-overlay:#1a2234;--color-overlay-black:rgba(0,0,0,.9);--color-overlay-muted:rgba(15,23,42,.55);--color-overlay-strong:rgba(15,23,42,.75);--color-overlay-darker:rgba(15,23,42,.88);--color-overlay-panel:rgba(26,34,52,.82);--color-overlay-panel-soft:rgba(26,34,52,.65);--color-overlay-panel-tint:rgba(31,41,55,.5);--color-overlay-panel-glass:rgba(28,37,58,.72);--color-overlay-panel-glass-soft:rgba(31,41,55,.55);--color-modal-backdrop:rgba(8,12,24,.58);--color-modal-surface:rgba(20,28,46,.78);--color-overlay-veil:rgba(11,17,32,.96);--color-overlay-banner:rgba(51,65,85,.95);--color-sidebar-surface:rgba(17,24,39,.94);--color-sidebar-surface-alt:rgba(11,22,48,.78);--color-sidebar-border:rgba(148,163,184,.28);--color-sidebar-border-strong:rgba(148,163,184,.42);--color-sidebar-hover:rgba(56,189,248,.18);--color-sidebar-hover-strong:rgba(129,140,248,.28);--color-sidebar-glow:rgba(56,189,248,.35);--color-sidebar-accent-start:rgba(56,189,248,.9);--color-sidebar-accent-end:rgba(129,140,248,.85);--color-sidebar-text-muted:rgba(203,213,225,.75);--color-overlay-panel-gradient-start:rgba(30,41,59,.92);--color-overlay-panel-gradient-end:rgba(15,23,42,.92);--color-surface-tint-subtle:rgba(148,163,184,.08);--color-border-translucent:rgba(148,163,184,.18);--color-border-translucent-strong:rgba(148,163,184,.25);--color-border-translucent-bright:rgba(148,163,184,.32);--color-border-translucent-stronger:rgba(148,163,184,.35);--color-border-translucent-strongest:rgba(148,163,184,.4);--color-border-translucent-glow:rgba(148,163,184,.55);--color-border-translucent-medium:rgba(148,163,184,.2);--color-border-neutral-soft:rgba(229,231,235,.35);--color-border-info-soft:rgba(96,165,250,.35);--color-border-sidebar-strong:rgba(55,65,81,.9);--color-ring-private:rgba(168,85,247,.55);--color-ring-private-hover:rgba(192,132,252,.65);--color-ring-critical:rgba(239,68,68,.4);--color-ring-critical-soft:rgba(239,68,68,.35);--color-ring-critical-hover:rgba(239,68,68,.45);--color-ring-critical-strong:rgba(239,68,68,.55);--color-ring-alert:rgba(220,38,38,.6);--color-gradient-visibility-start:rgba(129,140,248,.4);--color-gradient-visibility-end:rgba(192,132,252,.32);--color-white-soft:hsla(0,0%,100%,.1);--color-white-softer:hsla(0,0%,100%,.3);--color-white-softest:hsla(0,0%,100%,.4);--color-shadow-floating:rgba(8,11,30,.55);--color-shadow-elevated:rgba(8,11,30,.45);--color-shadow-panel:rgba(15,23,42,.35);--color-shadow-notification:rgba(15,23,42,.3);--color-shadow-intense:rgba(250,204,21,.48);--color-shadow-sidebar:rgba(0,0,0,.45);--color-divider-strong:rgba(15,23,42,.35);--color-text-frosted-strong:rgba(226,232,240,.94);--color-text-frosted:rgba(226,232,240,.9);--color-text-frosted-soft:rgba(226,232,240,.82);--scrollbar-width:0.5rem;--scrollbar-track:var(--color-white-soft);--scrollbar-thumb:var(--color-white-softer);--scrollbar-thumb-hover:var(--color-white-softest);--z-sidebar-overlay:10;--z-sidebar:20;--z-modal-root:100;--z-modal-overlay:110;--z-modal-content:120;--z-modal-player:130;--sidebar-width-expanded:16rem;--sidebar-width-collapsed:4rem;--sidebar-width:var(--sidebar-width-expanded);--sidebar-control-size:2.75rem;--sidebar-content-gap:1rem;--space-sidebar-shell-inline:var(--space-lg);--space-sidebar-shell-block-start:var(--space-lg);--space-sidebar-shell-block-end:1.75rem;--space-sidebar-scroll-gap:1.75rem;--space-sidebar-panel-gap:1.35rem;--space-sidebar-panel-padding-block:1.35rem;--space-sidebar-panel-padding-inline:1.1rem;--space-sidebar-footer-gap:0.85rem;--space-sidebar-trigger-padding-block:0.78rem;--space-sidebar-popover-offset:0.85rem;--space-sidebar-popover-padding-block:0.85rem;--space-sidebar-popover-padding-inline:0.65rem;--space-sidebar-popover-gap:0.45rem;--space-sidebar-nav-padding-block:0.7rem;--space-sidebar-subnaLine truncated
|
||||
|
||||
/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-family-sans);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--font-family-mono);font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{font-size:100%}@media (min-width:1024px){html{font-size:75%}}*,:after,:before{scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) transparent}::-webkit-scrollbar{width:var(--scrollbar-width);height:var(--scrollbar-width)}::-webkit-scrollbar-track{background:var(--scrollbar-track)}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:var(--radius-sm)}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}body{font-family:var(--font-family-sans);background-color:var(--color-page);color:var(--color-text-strong);line-height:1.5;margin:0;padding:0;overflow-x:hidden}#editNip71TagsFieldset>legend,#nip71TagsFieldset>legend{margin-left:0;padding:0}#app{background-color:var(--color-page);color:inherit}body.modal-open,html.modal-open{overflow:hidden;height:100%}body.modal-open #app{overflow:hidden}body.modal-open #sidebar,body.modal-open #sidebarOverlay{pointer-events:none;opacity:0;visibility:hidden}body.modal-open #sidebar{z-index:var(--z-sidebar-overlay)}body.modal-open #sidebarOverlay{z-index:calc(var(--z-sidebar-overlay) - 1)}.ng-cloak,.ng-hide:not(.ng-hide-animate),.x-ng-cloak,[data-ng-cloak],[ng-cloak],[ng\:cloak],[x-ng-cloak]{display:none!important}ng\:form{display:block}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}#playerModal.ds-header{display:flex}.ds-header{display:flex;align-items:center;justify-content:flex-start;padding-top:var(--space-md);padding-bottom:var(--space-md)}.focus-ring{outline:2px solid transparent;outline-offset:2px}.focus-ring:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--color-info);--tw-ring-opacity:0.7;--tw-ring-offset-width:2px;--tw-ring-offset-color:var(--color-surface);transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-fast);transition-timing-functionLine truncated
|
||||
/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-family-sans);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--font-family-mono);font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{font-size:100%}@media (min-width:1024px){html{font-size:75%}}*,:after,:before{scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) transparent}::-webkit-scrollbar{width:var(--scrollbar-width);height:var(--scrollbar-width)}::-webkit-scrollbar-track{background:var(--scrollbar-track)}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:var(--radius-sm)}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}body{font-family:var(--font-family-sans);background-color:var(--color-page);color:var(--color-text-strong);line-height:1.5;margin:0;padding:0;overflow-x:hidden}#editNip71TagsFieldset>legend,#nip71TagsFieldset>legend{margin-left:0;padding:0}#app{background-color:var(--color-page);color:inherit}body.modal-open,html.modal-open{overflow:hidden;height:100%}body.modal-open #app{overflow:hidden}body.modal-open #sidebar,body.modal-open #sidebarOverlay{pointer-events:none;opacity:0;visibility:hidden}body.modal-open #sidebar{z-index:var(--z-sidebar-overlay)}body.modal-open #sidebarOverlay{z-index:calc(var(--z-sidebar-overlay) - 1)}.ng-cloak,.ng-hide:not(.ng-hide-animate),.x-ng-cloak,[data-ng-cloak],[ng-cloak],[ng\:cloak],[x-ng-cloak]{display:none!important}ng\:form{display:block}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}#playerModal.ds-header{display:flex}.ds-header{display:flex;align-items:center;justify-content:flex-start;padding-top:var(--space-md);padding-bottom:var(--space-md)}.focus-ring{outline:2px solid transparent;outline-offset:2px}.focus-ring:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--color-info);--tw-ring-opacity:0.7;--tw-ring-offset-width:2px;--tw-ring-offset-color:var(--color-surface);transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-fast);transition-timing-functionLine truncated
|
||||
+10
-7
@@ -245,7 +245,7 @@
|
||||
}
|
||||
|
||||
.popover__panel {
|
||||
@apply focus-ring absolute left-0 top-full z-[70] mt-2 min-w-menu origin-top rounded-lg border border-border border-opacity-40 bg-panel px-4 py-3 text-sm text-text shadow-lg transition-transform transition-opacity duration-[var(--motion-duration-base)] ease-[var(--motion-ease-out)] opacity-0 scale-95 pointer-events-none;
|
||||
@apply focus-ring absolute left-0 top-full z-[70] mt-2 min-w-menu origin-top rounded-lg border border-border border-opacity-40 bg-panel px-popup-inline py-popup-block text-sm text-text shadow-lg transition-transform transition-opacity duration-[var(--motion-duration-base)] ease-[var(--motion-ease-out)] opacity-0 scale-95 pointer-events-none;
|
||||
}
|
||||
|
||||
.popover__panel[data-state="open"] {
|
||||
@@ -257,8 +257,8 @@
|
||||
color: var(--color-subtle);
|
||||
border: 1px solid var(--color-border-translucent-stronger);
|
||||
box-shadow: 0 22px 48px -18px var(--color-shadow-intense);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
backdrop-filter: blur(var(--popover-backdrop-blur));
|
||||
-webkit-backdrop-filter: blur(var(--popover-backdrop-blur));
|
||||
}
|
||||
|
||||
.btn {
|
||||
@@ -945,7 +945,7 @@
|
||||
|
||||
.bv-grid-video {
|
||||
@apply grid gap-xl py-lg;
|
||||
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(var(--grid-card-min-width), 1fr));
|
||||
}
|
||||
|
||||
.card[data-alert="nsfw-owner"] {
|
||||
@@ -1297,7 +1297,7 @@
|
||||
transition-property: opacity, transform;
|
||||
will-change: opacity, transform;
|
||||
opacity: 0;
|
||||
transform: translateY(0.75rem);
|
||||
transform: translateY(var(--toast-translate));
|
||||
}
|
||||
|
||||
.beacon-toast-motion[data-beacon-motion="enter"],
|
||||
@@ -1311,7 +1311,7 @@
|
||||
.beacon-toast-motion[data-beacon-motion="exit"],
|
||||
.torrent-toast-motion[data-beacon-motion="exit"] {
|
||||
opacity: 0;
|
||||
transform: translateY(0.75rem);
|
||||
transform: translateY(var(--toast-translate));
|
||||
}
|
||||
|
||||
.torrent-toast {
|
||||
@@ -2359,7 +2359,10 @@
|
||||
@media (max-width: 767px) {
|
||||
#sidebar {
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
width: min(var(--sidebar-width-expanded), calc(100vw - 3rem));
|
||||
width: min(
|
||||
var(--sidebar-width-expanded),
|
||||
calc(100vw - var(--sidebar-mobile-gutter))
|
||||
);
|
||||
}
|
||||
|
||||
#sidebar.sidebar-open {
|
||||
|
||||
@@ -134,6 +134,12 @@
|
||||
--space-sidebar-nav-padding-block: 0.7rem;
|
||||
--space-sidebar-subnav-padding-block: 0.55rem;
|
||||
--space-sidebar-nav-translate: 0.35rem;
|
||||
--grid-card-min-width: 20rem;
|
||||
--popover-backdrop-blur: 12px;
|
||||
--toast-translate: 0.75rem;
|
||||
--sidebar-mobile-gutter: 3rem;
|
||||
--popup-padding-inline: 1rem;
|
||||
--popup-padding-block: 0.75rem;
|
||||
--blog-embed-height: 100rem;
|
||||
|
||||
/* Progress primitives */
|
||||
|
||||
+14
-2
@@ -13,6 +13,10 @@ import { getApplication } from "./applicationContext.js";
|
||||
import { escapeHTML } from "./utils/domUtils.js";
|
||||
import positionFloatingPanel from "./ui/utils/positionFloatingPanel.js";
|
||||
import { createFloatingPanelStyles } from "./ui/utils/floatingPanelStyles.js";
|
||||
import {
|
||||
getPopupOffsetPx,
|
||||
getPopupViewportPaddingPx,
|
||||
} from "./designSystem/metrics.js";
|
||||
import { VideoCard } from "./ui/components/VideoCard.js";
|
||||
import { ALLOW_NSFW_CONTENT } from "./config.js";
|
||||
import {
|
||||
@@ -399,11 +403,19 @@ function openZapControls({ focus = false } = {}) {
|
||||
}
|
||||
if (!zapControlsPositioner) {
|
||||
const styles = createFloatingPanelStyles(controls);
|
||||
const metricsDocument =
|
||||
controls?.ownerDocument ||
|
||||
zapButton?.ownerDocument ||
|
||||
(typeof document !== "undefined" ? document : null);
|
||||
const offset = getPopupOffsetPx({ documentRef: metricsDocument });
|
||||
const viewportPadding = getPopupViewportPaddingPx({
|
||||
documentRef: metricsDocument,
|
||||
});
|
||||
zapControlsPositioner = positionFloatingPanel(zapButton, controls, {
|
||||
placement: "bottom",
|
||||
alignment: "end",
|
||||
offset: 8,
|
||||
viewportPadding: 16,
|
||||
offset,
|
||||
viewportPadding,
|
||||
styles,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
const TOKEN_FALLBACKS = {
|
||||
"--grid-card-min-width": "20rem",
|
||||
"--popover-backdrop-blur": "12px",
|
||||
"--toast-translate": "0.75rem",
|
||||
"--sidebar-mobile-gutter": "3rem",
|
||||
"--popup-padding-inline": "1rem",
|
||||
"--popup-padding-block": "0.75rem",
|
||||
};
|
||||
|
||||
function resolveDocument(documentRef) {
|
||||
if (documentRef && typeof documentRef === "object") {
|
||||
return documentRef;
|
||||
}
|
||||
return typeof globalThis.document === "object" ? globalThis.document : null;
|
||||
}
|
||||
|
||||
function readComputedStyle(documentRef) {
|
||||
const doc = resolveDocument(documentRef);
|
||||
if (!doc || !doc.documentElement) {
|
||||
return null;
|
||||
}
|
||||
const view = doc.defaultView || globalThis;
|
||||
if (!view || typeof view.getComputedStyle !== "function") {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return view.getComputedStyle(doc.documentElement);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function readTokenFallback(tokenName) {
|
||||
const fallback = TOKEN_FALLBACKS[tokenName];
|
||||
return typeof fallback === "string" ? fallback : "";
|
||||
}
|
||||
|
||||
function sanitizeTokenName(tokenName) {
|
||||
return typeof tokenName === "string" && tokenName.trim().startsWith("--")
|
||||
? tokenName.trim()
|
||||
: "";
|
||||
}
|
||||
|
||||
function normalizeTokenValue(value) {
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
export function readDesignToken(tokenName, { documentRef } = {}) {
|
||||
const token = sanitizeTokenName(tokenName);
|
||||
if (!token) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const doc = resolveDocument(documentRef);
|
||||
const computed = readComputedStyle(doc);
|
||||
if (computed) {
|
||||
const computedValue = normalizeTokenValue(computed.getPropertyValue(token));
|
||||
if (computedValue) {
|
||||
return computedValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (doc?.documentElement?.style) {
|
||||
const inlineValue = normalizeTokenValue(
|
||||
doc.documentElement.style.getPropertyValue(token),
|
||||
);
|
||||
if (inlineValue) {
|
||||
return inlineValue;
|
||||
}
|
||||
}
|
||||
|
||||
return readTokenFallback(token);
|
||||
}
|
||||
|
||||
function parsePixelValue(value) {
|
||||
const numeric = Number.parseFloat(value);
|
||||
return Number.isFinite(numeric) ? numeric : 0;
|
||||
}
|
||||
|
||||
function convertLengthToPixels(value, documentRef) {
|
||||
const raw = normalizeTokenValue(value);
|
||||
if (!raw) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (/^-?\d*\.?\d+px$/i.test(raw)) {
|
||||
return parsePixelValue(raw);
|
||||
}
|
||||
|
||||
if (/^-?\d*\.?\d+rem$/i.test(raw)) {
|
||||
const rem = parsePixelValue(raw);
|
||||
const doc = resolveDocument(documentRef);
|
||||
let base = 16;
|
||||
const view = doc?.defaultView || globalThis;
|
||||
if (doc?.documentElement && view && typeof view.getComputedStyle === "function") {
|
||||
try {
|
||||
const computed = view.getComputedStyle(doc.documentElement);
|
||||
const fontSize = normalizeTokenValue(computed?.fontSize || "");
|
||||
const parsed = parsePixelValue(fontSize);
|
||||
if (parsed > 0) {
|
||||
base = parsed;
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore computed style errors and fall back to defaults.
|
||||
}
|
||||
}
|
||||
return rem * base;
|
||||
}
|
||||
|
||||
const doc = resolveDocument(documentRef);
|
||||
if (doc?.createElement && doc?.body) {
|
||||
try {
|
||||
const probe = doc.createElement("div");
|
||||
probe.style.position = "absolute";
|
||||
probe.style.visibility = "hidden";
|
||||
probe.style.width = "0";
|
||||
probe.style.height = raw;
|
||||
doc.body.appendChild(probe);
|
||||
const measured = probe.offsetHeight;
|
||||
doc.body.removeChild(probe);
|
||||
if (Number.isFinite(measured)) {
|
||||
return measured;
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore measurement errors and fall through to numeric parsing.
|
||||
}
|
||||
}
|
||||
|
||||
return parsePixelValue(raw);
|
||||
}
|
||||
|
||||
export function readDesignTokenAsPixels(tokenName, options = {}) {
|
||||
const raw = readDesignToken(tokenName, options);
|
||||
return convertLengthToPixels(raw, options.documentRef);
|
||||
}
|
||||
|
||||
export function getGridCardMinWidth(options = {}) {
|
||||
return readDesignToken("--grid-card-min-width", options);
|
||||
}
|
||||
|
||||
export function getGridCardMinWidthPx(options = {}) {
|
||||
return readDesignTokenAsPixels("--grid-card-min-width", options);
|
||||
}
|
||||
|
||||
export function getPopoverBackdropBlur(options = {}) {
|
||||
return readDesignToken("--popover-backdrop-blur", options);
|
||||
}
|
||||
|
||||
export function getToastTranslate(options = {}) {
|
||||
return readDesignToken("--toast-translate", options);
|
||||
}
|
||||
|
||||
export function getToastTranslatePx(options = {}) {
|
||||
return readDesignTokenAsPixels("--toast-translate", options);
|
||||
}
|
||||
|
||||
export function getSidebarMobileGutter(options = {}) {
|
||||
return readDesignToken("--sidebar-mobile-gutter", options);
|
||||
}
|
||||
|
||||
export function getSidebarMobileGutterPx(options = {}) {
|
||||
return readDesignTokenAsPixels("--sidebar-mobile-gutter", options);
|
||||
}
|
||||
|
||||
export function getPopupPaddingInline(options = {}) {
|
||||
return readDesignToken("--popup-padding-inline", options);
|
||||
}
|
||||
|
||||
export function getPopupPaddingInlinePx(options = {}) {
|
||||
return readDesignTokenAsPixels("--popup-padding-inline", options);
|
||||
}
|
||||
|
||||
export function getPopupPaddingBlock(options = {}) {
|
||||
return readDesignToken("--popup-padding-block", options);
|
||||
}
|
||||
|
||||
export function getPopupPaddingBlockPx(options = {}) {
|
||||
return readDesignTokenAsPixels("--popup-padding-block", options);
|
||||
}
|
||||
|
||||
export function getPopupOffsetPx(options = {}) {
|
||||
const inlinePadding = getPopupPaddingInlinePx(options);
|
||||
return inlinePadding > 0 ? inlinePadding / 2 : 0;
|
||||
}
|
||||
|
||||
export function getPopupViewportPaddingPx(options = {}) {
|
||||
return getPopupPaddingInlinePx(options);
|
||||
}
|
||||
|
||||
export const designMetrics = {
|
||||
readDesignToken,
|
||||
readDesignTokenAsPixels,
|
||||
getGridCardMinWidth,
|
||||
getGridCardMinWidthPx,
|
||||
getPopoverBackdropBlur,
|
||||
getToastTranslate,
|
||||
getToastTranslatePx,
|
||||
getSidebarMobileGutter,
|
||||
getSidebarMobileGutterPx,
|
||||
getPopupPaddingInline,
|
||||
getPopupPaddingInlinePx,
|
||||
getPopupPaddingBlock,
|
||||
getPopupPaddingBlockPx,
|
||||
getPopupOffsetPx,
|
||||
getPopupViewportPaddingPx,
|
||||
};
|
||||
|
||||
export default designMetrics;
|
||||
@@ -1,6 +1,10 @@
|
||||
import { normalizeDesignSystemContext } from "../../designSystem.js";
|
||||
import positionFloatingPanel from "../utils/positionFloatingPanel.js";
|
||||
import { createFloatingPanelStyles } from "../utils/floatingPanelStyles.js";
|
||||
import {
|
||||
getPopupOffsetPx,
|
||||
getPopupViewportPaddingPx,
|
||||
} from "../../designSystem/metrics.js";
|
||||
|
||||
export class VideoCard {
|
||||
constructor({
|
||||
@@ -663,14 +667,25 @@ export class VideoCard {
|
||||
this.settingsButton = button;
|
||||
this.settingsDropdown = dropdown;
|
||||
this.settingsDropdownStyles = createFloatingPanelStyles(dropdown);
|
||||
const metricsDocument =
|
||||
dropdown?.ownerDocument ||
|
||||
button?.ownerDocument ||
|
||||
this.document ||
|
||||
(typeof document !== "undefined" ? document : null);
|
||||
const settingsOffset = getPopupOffsetPx({
|
||||
documentRef: metricsDocument,
|
||||
});
|
||||
const settingsViewportPadding = getPopupViewportPaddingPx({
|
||||
documentRef: metricsDocument,
|
||||
});
|
||||
this.settingsDropdownPositioner = positionFloatingPanel(
|
||||
button,
|
||||
dropdown,
|
||||
{
|
||||
placement: "bottom",
|
||||
alignment: "end",
|
||||
offset: 8,
|
||||
viewportPadding: 16,
|
||||
offset: settingsOffset,
|
||||
viewportPadding: settingsViewportPadding,
|
||||
styles: this.settingsDropdownStyles,
|
||||
},
|
||||
);
|
||||
@@ -877,11 +892,22 @@ export class VideoCard {
|
||||
this.moreMenuButton = button;
|
||||
this.moreMenu = dropdown;
|
||||
this.moreMenuStyles = createFloatingPanelStyles(dropdown);
|
||||
const menuMetricsDocument =
|
||||
dropdown?.ownerDocument ||
|
||||
button?.ownerDocument ||
|
||||
this.document ||
|
||||
(typeof document !== "undefined" ? document : null);
|
||||
const menuOffset = getPopupOffsetPx({
|
||||
documentRef: menuMetricsDocument,
|
||||
});
|
||||
const menuViewportPadding = getPopupViewportPaddingPx({
|
||||
documentRef: menuMetricsDocument,
|
||||
});
|
||||
this.moreMenuPositioner = positionFloatingPanel(button, dropdown, {
|
||||
placement: "bottom",
|
||||
alignment: "end",
|
||||
offset: 8,
|
||||
viewportPadding: 16,
|
||||
offset: menuOffset,
|
||||
viewportPadding: menuViewportPadding,
|
||||
styles: this.moreMenuStyles,
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import { createModalAccessibility } from "./modalAccessibility.js";
|
||||
import positionFloatingPanel from "../utils/positionFloatingPanel.js";
|
||||
import { createFloatingPanelStyles } from "../utils/floatingPanelStyles.js";
|
||||
import { applyDesignSystemAttributes } from "../../designSystem.js";
|
||||
import {
|
||||
getPopupOffsetPx,
|
||||
getPopupViewportPaddingPx,
|
||||
} from "../../designSystem/metrics.js";
|
||||
|
||||
export class VideoModal {
|
||||
constructor({
|
||||
@@ -298,14 +302,25 @@ export class VideoModal {
|
||||
}
|
||||
if (this.modalZapBtn) {
|
||||
this.modalZapStyles = createFloatingPanelStyles(this.modalZapDialog);
|
||||
const zapMetricsDocument =
|
||||
this.modalZapDialog?.ownerDocument ||
|
||||
this.modalZapBtn?.ownerDocument ||
|
||||
this.document ||
|
||||
(typeof document !== "undefined" ? document : null);
|
||||
const zapOffset = getPopupOffsetPx({
|
||||
documentRef: zapMetricsDocument,
|
||||
});
|
||||
const zapViewportPadding = getPopupViewportPaddingPx({
|
||||
documentRef: zapMetricsDocument,
|
||||
});
|
||||
this.modalZapPositioner = positionFloatingPanel(
|
||||
this.modalZapBtn,
|
||||
this.modalZapDialog,
|
||||
{
|
||||
placement: "bottom",
|
||||
alignment: "end",
|
||||
offset: 8,
|
||||
viewportPadding: 16,
|
||||
offset: zapOffset,
|
||||
viewportPadding: zapViewportPadding,
|
||||
styles: this.modalZapStyles,
|
||||
},
|
||||
);
|
||||
@@ -325,14 +340,25 @@ export class VideoModal {
|
||||
}
|
||||
if (this.modalMoreBtn) {
|
||||
this.modalMoreMenuStyles = createFloatingPanelStyles(this.modalMoreMenu);
|
||||
const menuMetricsDocument =
|
||||
this.modalMoreMenu?.ownerDocument ||
|
||||
this.modalMoreBtn?.ownerDocument ||
|
||||
this.document ||
|
||||
(typeof document !== "undefined" ? document : null);
|
||||
const menuOffset = getPopupOffsetPx({
|
||||
documentRef: menuMetricsDocument,
|
||||
});
|
||||
const menuViewportPadding = getPopupViewportPaddingPx({
|
||||
documentRef: menuMetricsDocument,
|
||||
});
|
||||
this.modalMoreMenuPositioner = positionFloatingPanel(
|
||||
this.modalMoreBtn,
|
||||
this.modalMoreMenu,
|
||||
{
|
||||
placement: "bottom",
|
||||
alignment: "end",
|
||||
offset: 8,
|
||||
viewportPadding: 16,
|
||||
offset: menuOffset,
|
||||
viewportPadding: menuViewportPadding,
|
||||
styles: this.modalMoreMenuStyles,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { normalizeDesignSystemContext } from "../designSystem.js";
|
||||
import positionFloatingPanel from "./utils/positionFloatingPanel.js";
|
||||
import { createFloatingPanelStyles } from "./utils/floatingPanelStyles.js";
|
||||
import {
|
||||
getPopupOffsetPx,
|
||||
getPopupViewportPaddingPx,
|
||||
} from "../designSystem/metrics.js";
|
||||
|
||||
export default class MoreMenuController {
|
||||
constructor(options = {}) {
|
||||
@@ -289,11 +293,20 @@ export default class MoreMenuController {
|
||||
|
||||
if (dropdown && !this.dropdownPositioners.has(dropdown)) {
|
||||
const styles = createFloatingPanelStyles(dropdown);
|
||||
const metricsDocument =
|
||||
dropdown?.ownerDocument ||
|
||||
button?.ownerDocument ||
|
||||
this.document ||
|
||||
(typeof document !== "undefined" ? document : null);
|
||||
const offset = getPopupOffsetPx({ documentRef: metricsDocument });
|
||||
const viewportPadding = getPopupViewportPaddingPx({
|
||||
documentRef: metricsDocument,
|
||||
});
|
||||
const positioner = positionFloatingPanel(button, dropdown, {
|
||||
placement: "bottom",
|
||||
alignment: "end",
|
||||
offset: 8,
|
||||
viewportPadding: 16,
|
||||
offset,
|
||||
viewportPadding,
|
||||
styles,
|
||||
});
|
||||
this.dropdownPositioners.set(dropdown, positioner);
|
||||
|
||||
+2
-1
@@ -9,10 +9,11 @@
|
||||
"build:beacon": "npm run build:beacon:bundle && npm run lint:inline-styles",
|
||||
"ci:beacon": "npm run build:beacon",
|
||||
"format": "prettier --cache --write css/tailwind.source.css css/tokens.css \"docs/**/*.html\" tailwind.config.cjs README.md",
|
||||
"lint": "npm run lint:css && npm run lint:hex && npm run lint:inline-styles",
|
||||
"lint": "npm run lint:css && npm run lint:hex && npm run lint:inline-styles && npm run lint:tokens",
|
||||
"lint:css": "stylelint css/tailwind.source.css css/tokens.css",
|
||||
"lint:hex": "node scripts/check-hex.js",
|
||||
"lint:inline-styles": "node scripts/check-inline-styles.mjs",
|
||||
"lint:tokens": "node scripts/check-design-tokens.mjs",
|
||||
"check:css": "npm run build:css && git diff --exit-code -- css/tailwind.generated.css",
|
||||
"test:visual": "npm run build:css && npx playwright test",
|
||||
"test:visual:update": "npm run build:css && cross-env UPDATE_VISUAL_BASELINES=1 npx playwright test",
|
||||
|
||||
Executable
+168
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { readdir, readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const repoRoot = path.resolve(__dirname, "..");
|
||||
|
||||
const TARGET_DIRECTORIES = ["js/ui"];
|
||||
const TARGET_FILES = ["js/channelProfile.js"];
|
||||
const IGNORED_EXTENSIONS = new Set([".min.js", ".map"]);
|
||||
const ALLOWED_FILES = new Set(["js/ui/components/RevertModal.js"]);
|
||||
const LENGTH_PATTERN = /\d+(?:\.\d+)?(?:px|rem)\b/gim;
|
||||
|
||||
function toPosix(relPath) {
|
||||
return relPath.split(path.sep).join("/");
|
||||
}
|
||||
|
||||
async function collectTargetFiles() {
|
||||
const results = new Set();
|
||||
|
||||
for (const target of TARGET_DIRECTORIES) {
|
||||
const absolute = path.resolve(repoRoot, target);
|
||||
await walkDirectory(absolute, results);
|
||||
}
|
||||
|
||||
for (const file of TARGET_FILES) {
|
||||
const absolute = path.resolve(repoRoot, file);
|
||||
results.add(absolute);
|
||||
}
|
||||
|
||||
return [...results];
|
||||
}
|
||||
|
||||
async function walkDirectory(dir, results) {
|
||||
let entries = [];
|
||||
try {
|
||||
entries = await readdir(dir, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
await walkDirectory(fullPath, results);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!entry.isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ext = path.extname(entry.name).toLowerCase();
|
||||
if (!ext || IGNORED_EXTENSIONS.has(ext)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
results.add(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
function buildLineBreaks(content) {
|
||||
const breaks = [];
|
||||
let index = content.indexOf("\n");
|
||||
while (index !== -1) {
|
||||
breaks.push(index);
|
||||
index = content.indexOf("\n", index + 1);
|
||||
}
|
||||
return breaks;
|
||||
}
|
||||
|
||||
function getLineNumber(lineBreaks, index) {
|
||||
let low = 0;
|
||||
let high = lineBreaks.length;
|
||||
while (low < high) {
|
||||
const mid = Math.floor((low + high) / 2);
|
||||
if (index <= lineBreaks[mid]) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
return low + 1;
|
||||
}
|
||||
|
||||
function getLineSnippet(content, index) {
|
||||
const start = content.lastIndexOf("\n", index - 1) + 1;
|
||||
let end = content.indexOf("\n", index);
|
||||
if (end === -1) {
|
||||
end = content.length;
|
||||
}
|
||||
return content.slice(start, end).trim();
|
||||
}
|
||||
|
||||
function shouldSkipMatch(content, index) {
|
||||
const previousChar = index > 0 ? content[index - 1] : "";
|
||||
if (previousChar === "[") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function collectViolations(filePath, content) {
|
||||
const violations = [];
|
||||
const lineBreaks = buildLineBreaks(content);
|
||||
let match;
|
||||
LENGTH_PATTERN.lastIndex = 0;
|
||||
while ((match = LENGTH_PATTERN.exec(content))) {
|
||||
const index = match.index;
|
||||
if (shouldSkipMatch(content, index)) {
|
||||
continue;
|
||||
}
|
||||
const value = match[0];
|
||||
if (/^0+(?:\.0+)?(?:px|rem)$/i.test(value)) {
|
||||
continue;
|
||||
}
|
||||
violations.push({
|
||||
file: toPosix(path.relative(repoRoot, filePath)),
|
||||
line: getLineNumber(lineBreaks, index),
|
||||
value,
|
||||
snippet: getLineSnippet(content, index),
|
||||
});
|
||||
}
|
||||
return violations;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const files = await collectTargetFiles();
|
||||
const violations = [];
|
||||
|
||||
for (const filePath of files) {
|
||||
const rel = toPosix(path.relative(repoRoot, filePath));
|
||||
if (ALLOWED_FILES.has(rel)) {
|
||||
continue;
|
||||
}
|
||||
let content;
|
||||
try {
|
||||
content = await readFile(filePath, "utf8");
|
||||
} catch (error) {
|
||||
continue;
|
||||
}
|
||||
const fileViolations = collectViolations(filePath, content);
|
||||
violations.push(...fileViolations);
|
||||
}
|
||||
|
||||
if (violations.length > 0) {
|
||||
console.error("Design token lint failed: raw measurements detected outside tokens.\n");
|
||||
for (const violation of violations) {
|
||||
console.error(
|
||||
`${violation.file}:${violation.line} → ${violation.value}\n ${violation.snippet}`,
|
||||
);
|
||||
}
|
||||
console.error("\nMove these measurements into css/tokens.css or use the metrics helper.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Failed to check design tokens.");
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -41,6 +41,7 @@ const IGNORED_FILES = new Set([
|
||||
|
||||
const VIOLATION_ALLOWLIST = new Map([
|
||||
["js/ui/utils/floatingPanelStyles.js", new Set(["Direct .style usage"])],
|
||||
["js/designSystem/metrics.js", new Set(["Direct .style usage"])],
|
||||
]);
|
||||
|
||||
const TEXT_EXTENSIONS = new Set([
|
||||
|
||||
+12
-2
@@ -197,16 +197,26 @@ module.exports = {
|
||||
"sidebar-popover-padding-inline": "var(--space-sidebar-popover-padding-inline)",
|
||||
"sidebar-popover-gap": "var(--space-sidebar-popover-gap)",
|
||||
"sidebar-nav-padding-block": "var(--space-sidebar-nav-padding-block)",
|
||||
"sidebar-subnav-padding-block": "var(--space-sidebar-subnav-padding-block)"
|
||||
"sidebar-subnav-padding-block": "var(--space-sidebar-subnav-padding-block)",
|
||||
"sidebar-mobile-gutter": "var(--sidebar-mobile-gutter)",
|
||||
"popup-inline": "var(--popup-padding-inline)",
|
||||
"popup-block": "var(--popup-padding-block)"
|
||||
},
|
||||
minWidth: {
|
||||
menu: "var(--menu-min-width)"
|
||||
menu: "var(--menu-min-width)",
|
||||
"grid-card": "var(--grid-card-min-width)"
|
||||
},
|
||||
maxWidth: {
|
||||
modal: "var(--modal-max-width)",
|
||||
player: "var(--layout-player-max-width)",
|
||||
docs: "var(--layout-docs-max-width)"
|
||||
},
|
||||
translate: {
|
||||
toast: "var(--toast-translate)"
|
||||
},
|
||||
backdropBlur: {
|
||||
popover: "var(--popover-backdrop-blur)"
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ["var(--font-family-sans)"],
|
||||
mono: ["var(--font-family-mono)"]
|
||||
|
||||
Reference in new issue
Block a user