l0qp1n5qr: Drop the faded disabled sign-in override and guard against faded disabled buttons
This commit is contained in:
@@ -100,5 +100,4 @@
|
|||||||
}
|
}
|
||||||
.signin-field input::placeholder { color: var(--ink-3); }
|
.signin-field input::placeholder { color: var(--ink-3); }
|
||||||
.signin-form .signin-submit { margin-top: 18px; width: 100%; height: 54px; }
|
.signin-form .signin-submit { margin-top: 18px; width: 100%; height: 54px; }
|
||||||
.signin-form .signin-submit:disabled { opacity: .5; cursor: not-allowed; }
|
|
||||||
.signin-error { margin: 0; }
|
.signin-error { margin: 0; }
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
/** Every .css file under src/ (src/styles, src/app/globals.css, and any other). */
|
||||||
|
function cssFiles(dir: string): string[] {
|
||||||
|
return readdirSync(dir).flatMap((name) => {
|
||||||
|
const full = path.join(dir, name);
|
||||||
|
if (statSync(full).isDirectory()) return cssFiles(full);
|
||||||
|
return full.endsWith(".css") ? [full] : [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rules whose selector targets a disabled state and whose opacity is below 1. */
|
||||||
|
function fadedDisabledRules(source: string): string[] {
|
||||||
|
const text = source.replace(/\/\*[\s\S]*?\*\//g, "");
|
||||||
|
const found: string[] = [];
|
||||||
|
// Innermost blocks only: the selector text runs back to the previous brace,
|
||||||
|
// so rules nested inside @media or @supports are scanned too.
|
||||||
|
for (const m of text.matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
|
||||||
|
const selector = m[1].trim();
|
||||||
|
if (!/:disabled|\[disabled\]|\[aria-disabled(="true")?\]/.test(selector)) continue;
|
||||||
|
for (const o of m[2].matchAll(/(?:^|;|\s)opacity\s*:\s*([\d.]+)(%?)/g)) {
|
||||||
|
const value = parseFloat(o[1]) / (o[2] ? 100 : 1);
|
||||||
|
if (value < 1) found.push(`${selector} { opacity: ${o[1]}${o[2]} }`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("disabled buttons are never faded", () => {
|
||||||
|
const root = path.resolve(__dirname, "../src");
|
||||||
|
const files = cssFiles(root);
|
||||||
|
|
||||||
|
it("finds the stylesheets, globals.css included", () => {
|
||||||
|
expect(files.some((f) => f.endsWith(path.join("app", "globals.css")))).toBe(true);
|
||||||
|
expect(files.some((f) => f.endsWith(path.join("styles", "signin.css")))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("the scanner catches a faded disabled rule", () => {
|
||||||
|
expect(fadedDisabledRules(".a:disabled { opacity: .5; }")).toHaveLength(1);
|
||||||
|
expect(fadedDisabledRules("@media (x) { .a[disabled] { color: red; opacity: 40% } }")).toHaveLength(1);
|
||||||
|
expect(fadedDisabledRules(".a:disabled { opacity: 1; }")).toHaveLength(0);
|
||||||
|
expect(fadedDisabledRules(".a:hover { opacity: .5; }")).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
it(`${path.relative(root, file)} has no :disabled rule with opacity below 1`, () => {
|
||||||
|
expect(fadedDisabledRules(readFileSync(file, "utf8"))).toEqual([]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user