Merge pull request #548 from PR0M3TH3AN/codex/update-design-tokens-linting-script

Extend design token linting to Tailwind CSS
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-14 21:28:40 -04:00
commit b1f9cf2ee2
5 files changed
+155 -62

No files matched your search

+9
View File
@@ -100,6 +100,15 @@ and scripts for `style=` attributes, `element.style`, or `style.cssText` usage
and will fail CI until offending markup is moved into the shared CSS/token
system.
#### Design token guard
`npm run lint:tokens` inspects JavaScript **and** `css/tailwind.source.css`
for raw `px`/`rem` measurements. Prefer design tokens, Tailwind `theme()` calls,
or existing utilities over hard-coded lengths. When unavoidable (for example
hairline borders or browser reset quirks) add an explicit allowlist entry in
`scripts/check-design-tokens.mjs` so future contributors understand why the
constant exists.
Beacon builds inherit the same rule. `npm run build:beacon` now bundles
`torrent/dist` and immediately re-runs the inline-style checker against the
fresh output so third-party dependencies cannot sneak inline style mutations
+62 -58
View File
@@ -9,7 +9,7 @@
font-size: 100%;
}
@media (min-width: 1024px) {
@media (min-width: theme('screens.lg')) {
html {
font-size: 75%;
}
@@ -293,7 +293,7 @@
.popover__panel[data-variant="zap"] {
background: var(--color-overlay-veil);
color: var(--color-subtle);
border: 1px solid var(--color-border-translucent-stronger);
border: var(--border-width-hairline) solid var(--color-border-translucent-stronger);
box-shadow: var(--shadow-popover-zap);
backdrop-filter: blur(var(--popover-backdrop-blur));
-webkit-backdrop-filter: blur(var(--popover-backdrop-blur));
@@ -863,7 +863,7 @@
right var(--form-select-icon-hit-area) center;
background-size: var(--form-select-icon-size) var(--form-select-icon-size),
var(--form-select-icon-size) var(--form-select-icon-size),
1px 60%;
var(--form-select-divider-width) 60%;
background-repeat: no-repeat;
}
@@ -1205,12 +1205,12 @@
gap: var(--space-md);
padding: var(--space-md-plus) var(--space-lg);
border-radius: var(--radius-xl);
border: 1px solid var(--color-border-translucent);
border: var(--border-width-hairline) solid var(--color-border-translucent);
background-color: var(--color-surface);
box-shadow: var(--shadow-sm);
}
@media (max-width: 480px) {
@media (max-width: theme('screens.xs')) {
#playerModal .modal-nav .icon-button {
width: var(--space-xl);
height: var(--space-xl);
@@ -1260,7 +1260,7 @@
#playerModal .description-panel {
padding: var(--space-md-plus) var(--space-lg);
border-radius: var(--radius-xl);
border: 1px solid var(--color-border-translucent);
border: var(--border-width-hairline) solid var(--color-border-translucent);
background-color: var(--color-surface);
box-shadow: var(--shadow-sm);
}
@@ -1284,9 +1284,9 @@
var(--color-overlay-panel-gradient-start) 0%,
var(--color-overlay-panel-gradient-end) 100%
);
border-bottom: 1px solid var(--color-border-translucent);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
border-bottom: var(--border-width-hairline) solid var(--color-border-translucent);
backdrop-filter: blur(var(--modal-nav-backdrop-blur));
-webkit-backdrop-filter: blur(var(--modal-nav-backdrop-blur));
}
#playerModal .modal-nav__bar {
@@ -1307,7 +1307,7 @@
width: var(--icon-size-lg);
height: var(--icon-size-lg);
background: var(--color-surface-tint-subtle);
border: 1px solid var(--color-border-translucent-medium);
border: var(--border-width-hairline) solid var(--color-border-translucent-medium);
color: var(--color-text-muted);
}
@@ -1330,7 +1330,7 @@
@apply grid gap-6 lg:gap-8;
}
@media (min-width: 1024px) {
@media (min-width: theme('screens.lg')) {
.video-modal__layout {
grid-template-columns:
minmax(0, var(--video-modal-primary-fr))
@@ -1342,7 +1342,7 @@
@apply grid gap-6;
}
@media (min-width: 1024px) {
@media (min-width: theme('screens.lg')) {
.revert-modal__layout {
grid-template-columns:
minmax(var(--revert-modal-sidebar-min), var(--revert-modal-sidebar-width))
@@ -1364,7 +1364,7 @@
padding-block: clamp(var(--space-xs), 2.5vw, var(--space-md-plus));
}
@media (min-width: 1024px) {
@media (min-width: theme('screens.lg')) {
.video-modal__video {
position: sticky;
top: calc(var(--space-2xl) + var(--space-xl));
@@ -1398,7 +1398,8 @@
}
.video-modal__action-button {
@apply inline-flex h-11 min-w-[2.75rem] items-center justify-center gap-2 rounded-full border border-border border-opacity-30 bg-panel px-3 text-sm font-semibold text-text transition duration-base ease-out hover:bg-panel-hover;
@apply inline-flex h-11 items-center justify-center gap-2 rounded-full border border-border border-opacity-30 bg-panel px-3 text-sm font-semibold text-text transition duration-base ease-out hover:bg-panel-hover;
min-width: var(--video-modal-action-min-width);
}
.video-modal__action-label {
@@ -1444,7 +1445,7 @@
.torrent-clipboard-textarea {
position: fixed;
top: 0;
left: -9999px;
left: var(--offscreen-offset);
opacity: 0;
width: 0;
height: 0;
@@ -1648,7 +1649,7 @@
}
.watch-history-card__meta {
@apply flex min-w-0 flex-col justify-between gap-4 sm:min-w-[14rem] sm:flex-[0_0_16rem];
@apply flex min-w-0 flex-col justify-between gap-4 sm:min-w-56 sm:flex-none sm:basis-64;
}
.watch-history-card__watched {
@@ -2006,7 +2007,7 @@
body.iframe-form #status {
background: var(--color-surface);
border-radius: var(--radius-sm);
box-shadow: inset 0 0 0 1px var(--color-border-translucent);
box-shadow: inset 0 0 0 var(--border-width-hairline) var(--color-border-translucent);
color: var(--color-subtle-text);
min-height: calc(var(--space-2xl) + var(--space-xl));
padding: var(--space-sm);
@@ -2030,7 +2031,7 @@
color: var(--color-warning-strong);
}
@media (max-width: 640px) {
@media (max-width: theme('screens.sm')) {
body.iframe-form {
margin: var(--space-md);
}
@@ -2077,7 +2078,7 @@
var(--space-sidebar-shell-block-end);
width: 100%;
border-radius: var(--radius-sidebar-shell);
border: 1px solid var(--color-sidebar-border-strong);
border: var(--border-width-hairline) solid var(--color-sidebar-border-strong);
background:
radial-gradient(
140% 140% at 15% 10%,
@@ -2154,7 +2155,7 @@
width: var(--sidebar-control-size);
height: var(--sidebar-control-size);
border-radius: var(--radius-full);
border: 1px solid var(--color-border-translucent-medium);
border: var(--border-width-hairline) solid var(--color-border-translucent-medium);
background: var(--color-surface-tint-subtle);
color: inherit;
cursor: pointer;
@@ -2206,14 +2207,14 @@
text-decoration: none;
width: 100%;
position: relative;
border: 1px solid var(--color-sidebar-border);
border: var(--border-width-hairline) solid var(--color-sidebar-border);
background:
linear-gradient(
155deg,
color-mix(in srgb, var(--color-sidebar-surface) 72%, transparent) 0%,
color-mix(in srgb, var(--color-sidebar-surface-alt) 48%, transparent) 100%
);
box-shadow: inset 0 0 0 0.5px var(--color-sidebar-border);
box-shadow: inset 0 0 0 var(--border-width-fine) var(--color-sidebar-border);
transform: translateX(var(--sidebar-nav-translate-x));
transition: color 180ms ease, border-color 200ms ease, box-shadow 220ms ease,
transform 220ms ease;
@@ -2229,7 +2230,7 @@
.sidebar-nav-link::after {
content: "";
position: absolute;
inset: -1px;
inset: calc(var(--border-width-hairline) * -1);
border-radius: inherit;
pointer-events: none;
transition: opacity 220ms ease, transform 240ms ease;
@@ -2265,7 +2266,7 @@
--sidebar-nav-translate-x: var(--space-sidebar-nav-translate);
color: var(--color-white);
border-color: var(--color-sidebar-hover-strong);
box-shadow: 0 0 0 1px var(--color-sidebar-hover-strong),
box-shadow: 0 0 0 var(--border-width-hairline) var(--color-sidebar-hover-strong),
var(--shadow-sidebar-accent);
}
@@ -2317,7 +2318,7 @@
padding: var(--space-sidebar-trigger-padding-block) var(--space-md);
min-height: var(--sidebar-control-size);
border-radius: var(--radius-sidebar-nav);
border: 1px solid var(--color-sidebar-border);
border: var(--border-width-hairline) solid var(--color-sidebar-border);
background:
linear-gradient(
155deg,
@@ -2336,7 +2337,7 @@
saturate(var(--sidebar-trigger-backdrop-saturation));
-webkit-backdrop-filter: blur(var(--sidebar-backdrop-blur))
saturate(var(--sidebar-trigger-backdrop-saturation));
box-shadow: inset 0 0 0 0.5px var(--color-sidebar-border),
box-shadow: inset 0 0 0 var(--border-width-fine) var(--color-sidebar-border),
var(--shadow-sidebar-trigger);
transition: border-color 200ms ease, color 200ms ease, box-shadow 220ms ease,
background 220ms ease;
@@ -2345,7 +2346,7 @@
.sidebar-dropup-trigger::before {
content: "";
position: absolute;
inset: -1px;
inset: calc(var(--border-width-hairline) * -1);
border-radius: inherit;
pointer-events: none;
background: linear-gradient(
@@ -2363,7 +2364,7 @@
.sidebar-dropup-trigger::after {
content: "";
position: absolute;
inset: -1px;
inset: calc(var(--border-width-hairline) * -1);
border-radius: inherit;
pointer-events: none;
background: linear-gradient(110deg, var(--color-sidebar-hover) 0%, transparent 80%);
@@ -2377,7 +2378,7 @@
.sidebar-dropup-trigger:focus-visible {
color: var(--color-white);
border-color: var(--color-sidebar-hover-strong);
box-shadow: 0 0 0 1px var(--color-sidebar-hover-strong),
box-shadow: 0 0 0 var(--border-width-hairline) var(--color-sidebar-hover-strong),
var(--shadow-sidebar-dropup);
}
@@ -2420,14 +2421,14 @@
right: 0;
bottom: calc(100% + var(--space-sidebar-popover-offset));
border-radius: var(--radius-xl);
border: 1px solid var(--color-sidebar-border-strong);
border: var(--border-width-hairline) solid var(--color-sidebar-border-strong);
background:
linear-gradient(
160deg,
color-mix(in srgb, var(--color-sidebar-surface) 88%, transparent) 0%,
color-mix(in srgb, var(--color-sidebar-surface-alt) 68%, transparent) 100%
);
box-shadow: inset 0 0 0 0.5px var(--color-sidebar-border),
box-shadow: inset 0 0 0 var(--border-width-fine) var(--color-sidebar-border),
var(--shadow-sidebar-panel);
padding: var(--space-sidebar-popover-padding-block)
var(--space-sidebar-popover-padding-inline);
@@ -2470,7 +2471,7 @@
#sidebar.sidebar-open {
box-shadow: var(--shadow-sidebar-shell),
0 0 0 1px var(--color-sidebar-border-strong);
0 0 0 var(--border-width-hairline) var(--color-sidebar-border-strong);
}
#sidebarOverlay {
@@ -2507,7 +2508,9 @@
white-space: nowrap;
}
@media (max-width: 767px) {
@media (
max-width: calc(theme('screens.md') - theme('borderWidth.DEFAULT'))
) {
#sidebar {
transform: translate3d(-100%, 0, 0);
width: min(
@@ -2525,7 +2528,7 @@
}
}
@media (min-width: 768px) {
@media (min-width: theme('screens.md')) {
.sidebar-panel__header {
display: flex;
}
@@ -2674,7 +2677,7 @@
@keyframes card-motion-enter {
from {
opacity: 0;
transform: translateY(0.5rem);
transform: translateY(var(--motion-card-enter-translate));
}
to {
@@ -2706,7 +2709,7 @@
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(1.25rem);
transform: translateY(var(--motion-fade-in-translate));
}
to {
@@ -2723,7 +2726,7 @@
}
@layer utilities {
@media (max-width: 25rem) {
@media (max-width: theme('screens.compact')) {
.u-max-400-flex-wrap {
flex-wrap: wrap;
}
@@ -2762,7 +2765,7 @@
}
}
@media (max-width: 40rem) {
@media (max-width: theme('screens.sm')) {
.bv-grid-video {
gap: var(--space-md);
grid-template-columns: 1fr;
@@ -2854,7 +2857,8 @@
@layer components {
.ui-grid-row {
border-bottom: 1px solid var(--color-border-subtle, rgb(209 213 219));
border-bottom: var(--border-width-hairline)
solid var(--color-border-subtle, rgb(209 213 219));
}
.status-line {
@@ -2883,7 +2887,7 @@
font-size: var(--blog-font-size-root);
}
@media (max-width: 768px) {
@media (max-width: theme('screens.md')) {
.blog-html {
font-size: var(--font-size-lg);
}
@@ -2974,7 +2978,7 @@
color: var(--color-blog-gray-220);
}
@media (max-width: 768px) {
@media (max-width: theme('screens.md')) {
.blog-app-root .header .external-link {
margin-bottom: var(--space-md);
margin-right: var(--blog-space-inline-lg);
@@ -3115,13 +3119,13 @@
border-bottom: var(--space-xs) solid var(--color-blog-neutral-100);
}
@media (max-width: 768px) {
@media (max-width: theme('screens.md')) {
.blog-app-root .top-notes {
display: block;
}
}
@media (max-width: 768px) {
@media (max-width: theme('screens.md')) {
.blog-app-root .top-notes {
padding-bottom: 0;
}
@@ -3132,7 +3136,7 @@
box-sizing: border-box;
}
@media (max-width: 768px) {
@media (max-width: theme('screens.md')) {
.blog-app-root .top-notes .note {
margin-bottom: var(--space-xl);
}
@@ -3144,7 +3148,7 @@
.blog-app-root .top-notes .note img {
width: 100%;
height: 200px;
height: var(--blog-top-note-image-height);
object-fit: cover;
margin-bottom: var(--space-xs);
border-radius: var(--radius-sm);
@@ -3201,7 +3205,7 @@
color: var(--color-blog-gray-220);
}
@media (max-width: 768px) {
@media (max-width: theme('screens.md')) {
.blog-app-root .short-notes [data-carousel-control] {
display: none;
}
@@ -3285,7 +3289,7 @@
margin-top: 0;
}
@media (max-width: 768px) {
@media (max-width: theme('screens.md')) {
.blog-app-root h1 {
font-size: var(--blog-font-size-h1-mobile);
}
@@ -3388,7 +3392,7 @@
font-family: var(--ztr-font);
font-size: var(--ztr-font-size);
color: var(--ztr-text-color);
border: 1px solid transparent;
border: var(--border-width-hairline) solid transparent;
word-wrap: break-word;
margin-top: 4em;
line-height: 1.2em;
@@ -3490,7 +3494,7 @@
line-break: loose;
word-break: break-word;
overflow: hidden;
max-width: 750px;
max-width: var(--layout-blog-max-width);
}
.blog-app-root .ztr-comment-text p {
@@ -3615,7 +3619,7 @@
line-height: 1.4;
color: var(--color-blog-gray-800);
background-color: var(--color-blog-neutral-50);
border: 1px solid var(--color-blog-gray-220);
border: var(--border-width-hairline) solid var(--color-blog-gray-220);
outline: none;
width: calc(100% - 1.5em);
padding: 0.7em;
@@ -3778,8 +3782,8 @@
.blog-app-root .loader.svelte-181fgen {
display: inline-block;
width: 92px;
height: 92px;
width: var(--blog-loader-dimension-lg);
height: var(--blog-loader-dimension-lg);
opacity: 0;
animation:
svelte-181fgen-fade-in 0.5s 0.4s forwards,
@@ -3789,11 +3793,11 @@
.blog-app-root .loader.svelte-181fgen:after {
content: " ";
display: block;
width: 64px;
height: 64px;
width: var(--blog-loader-dimension-md);
height: var(--blog-loader-dimension-md);
margin: var(--space-xs);
border-radius: 50%;
border: 6px solid var(--color-blog-gray-275);
border: var(--blog-loader-border-width) solid var(--color-blog-gray-275);
border-color: var(--color-blog-gray-275) transparent var(--color-blog-gray-275) transparent;
animation: svelte-181fgen-loader-animation 2s linear infinite;
}
@@ -3815,7 +3819,7 @@
}
.dark .blog-app-root .loader.svelte-181fgen:after {
border: 6px solid var(--color-card);
border: var(--blog-loader-border-width) solid var(--color-card);
border-color: var(--color-card) transparent var(--color-card) transparent;
}
@@ -3836,7 +3840,7 @@
content: "";
}
@media (max-width: 768px) {
@media (max-width: theme('screens.md')) {
.blog-body #theme-toggle.svelte-bxrhh + label.svelte-bxrhh {
right: var(--space-md);
}
@@ -3848,7 +3852,7 @@
.blog-body #theme-toggle.svelte-bxrhh:checked + label.svelte-bxrhh {
background-color: transparent;
box-shadow: inset -6px -6px 1px 1px var(--color-card);
box-shadow: var(--blog-theme-toggle-shadow);
}
.blog-app-root .ztr-nowrap {
+21
View File
@@ -136,6 +136,7 @@
--blur-modal-backdrop: 28px;
--blur-modal-alert: 14px;
--blur-thumbnail-blur: 18px;
--modal-nav-backdrop-blur: 6px;
--border-default: var(--color-border);
--border-subtle: var(--color-border-subtle);
@@ -146,6 +147,8 @@
--border-neutral-soft: var(--color-border-neutral-soft);
--border-info-soft: var(--color-border-info-soft);
--border-sidebar-strong: var(--color-border-sidebar-strong);
--border-width-hairline: 1px;
--border-width-fine: 0.5px;
--text-primary: var(--color-text);
--text-inverse: var(--color-text-strong);
@@ -349,6 +352,13 @@
--blog-carousel-progress-radius: var(--progress-radius);
--blog-carousel-progress-track: var(--color-border-translucent);
--blog-carousel-progress-active: var(--color-blog-purple-500);
--blog-loader-border-width: 6px;
--blog-loader-dimension-lg: 92px;
--blog-loader-dimension-md: 64px;
--blog-theme-toggle-shadow-offset: 6px;
--blog-theme-toggle-shadow: inset calc(var(--blog-theme-toggle-shadow-offset) * -1)
calc(var(--blog-theme-toggle-shadow-offset) * -1)
var(--border-width-hairline) var(--border-width-hairline) var(--color-card);
/* Spacing scale */
--space-4xs: 0.0625rem;
@@ -392,6 +402,8 @@
--status-log-min-height: 5rem;
--layout-player-max-width: 68.75rem;
--layout-docs-max-width: 92.5rem;
--layout-blog-max-width: 46.875rem;
--blog-top-note-image-height: 12.5rem;
--icon-size-sm: var(--font-size-lg);
--icon-size-md: var(--space-md-plus);
--icon-size-lg: var(--space-xl-plus);
@@ -399,6 +411,7 @@
--form-textarea-min-height: 8rem;
--scrollbar-thin-width: 0.4rem;
--modal-progress-height: calc(var(--space-3xs) * 3);
--video-modal-action-min-width: 2.75rem;
--outline-thick-width: 0.1875rem;
--icon-button-ring-width: var(--outline-thick-width);
@@ -407,8 +420,16 @@
--form-select-icon-gap: var(--space-xs-snug);
--form-select-icon-hit-area: var(--space-xl-plus);
--form-select-icon-size: 0.6rem;
--form-select-divider-width: var(--border-width-hairline);
--form-checkbox-checkmark-offset: calc(var(--space-3xs) * -1);
/* Utility measurements */
--offscreen-offset: -9999px;
/* Motion */
--motion-card-enter-translate: var(--space-xs);
--motion-fade-in-translate: var(--space-md-plus);
/* Shadows */
--shadow-sm: 0 0.0625rem 0.125rem 0 rgb(0 0 0 / 5%);
--shadow-md: 0 0.25rem 0.375rem -0.0625rem rgb(0 0 0 / 10%);
+59 -4
View File
@@ -10,11 +10,31 @@ const __dirname = path.dirname(__filename);
const repoRoot = path.resolve(__dirname, "..");
const TARGET_DIRECTORIES = ["js/ui"];
const TARGET_FILES = ["js/channelProfile.js"];
const TARGET_FILES = ["js/channelProfile.js", "css/tailwind.source.css"];
const IGNORED_EXTENSIONS = new Set([".min.js", ".map"]);
const ALLOWED_FILES = new Set(["js/ui/components/RevertModal.js"]);
const ALLOWED_FILES = new Set([
"js/ui/components/RevertModal.js",
"css/tokens.css",
]);
const LENGTH_PATTERN = /\d+(?:\.\d+)?(?:px|rem)\b/gim;
const GLOBAL_VALUE_ALLOWLIST = [
(value) => /^0+(?:\.0+)?(?:px|rem)$/i.test(value),
];
const VALUE_ALLOWLIST = new Map([
[
"css/tailwind.source.css",
[
// Hairline borders and reset styles that intentionally rely on raw pixels.
(value, snippet) =>
/^0?\.5px$/i.test(value) && /box-shadow/i.test(snippet),
(value, snippet) =>
/^1px$/i.test(value) && /(border|box-shadow|outline)/i.test(snippet),
],
],
]);
const MARKUP_EXTENSIONS = new Set([".html", ".md", ".mdx", ".markdown", ".njk", ".nunjucks"]);
const MARKUP_IGNORED_DIRECTORIES = new Set(["node_modules", ".git"]);
const CLASS_ATTRIBUTE_PATTERN = /class\s*=\s*(["'])(.*?)\1/gis;
@@ -25,6 +45,40 @@ function toPosix(relPath) {
return relPath.split(path.sep).join("/");
}
function getAllowlistEntries(filePath) {
const entries = [...GLOBAL_VALUE_ALLOWLIST];
const relPath = toPosix(path.relative(repoRoot, filePath));
const fileSpecific = VALUE_ALLOWLIST.get(relPath);
if (fileSpecific) {
entries.push(...fileSpecific);
}
return entries;
}
function isValueAllowed(filePath, value, snippet) {
const entries = getAllowlistEntries(filePath);
for (const entry of entries) {
if (entry instanceof RegExp) {
if (entry.test(value)) {
return true;
}
continue;
}
if (typeof entry === "function") {
if (entry(value, snippet, filePath)) {
return true;
}
continue;
}
if (typeof entry === "string") {
if (entry.toLowerCase() === value.toLowerCase()) {
return true;
}
}
}
return false;
}
async function collectScriptFiles() {
const results = new Set();
@@ -174,14 +228,15 @@ function collectViolations(filePath, content) {
continue;
}
const value = match[0];
if (/^0+(?:\.0+)?(?:px|rem)$/i.test(value)) {
const snippet = getLineSnippet(content, index);
if (isValueAllowed(filePath, value, snippet)) {
continue;
}
violations.push({
file: toPosix(path.relative(repoRoot, filePath)),
line: getLineNumber(lineBreaks, index),
value,
snippet: getLineSnippet(content, index),
snippet,
});
}
return violations;
+4
View File
@@ -13,6 +13,10 @@ module.exports = {
safelist: ["bv-logo__background"],
theme: {
extend: {
screens: {
xs: "30rem",
compact: "25rem",
},
colors: {
page: "var(--color-page)",
"page-alt": "var(--color-page-alt)",