Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion demos/filter-drop-shadow.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
<div class="demo-yta swatch-solo"><span class="swatch f-drop f-motiv"><span class="swatch-yta"></span><span class="swatch-lbl">drop-shadow</span></span></div>
<style data-include="swatch swatch-solo filter-motiv"></style>
<style>
.f-drop .swatch-yta { filter: drop-shadow(0 0 8px var(--acc)) brightness(.9); }
.f-drop { overflow: visible; }
.f-drop .swatch-yta {
filter: drop-shadow(0 0 8px var(--acc)) brightness(.9);
border-radius: 9px 9px 0 0;
}
</style>
<style data-live>
/* Reglerbart filter: --lv (0–8) blir 0–16 px skuggradie. Labbet är sidans
Expand Down
12 changes: 10 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,11 @@
.labbar.f-invert .swatch-yta { filter: invert(calc(var(--lv) * 12.5%)); }
/* /demo:filter-invert:css */
/* demo:filter-drop-shadow:css */
.f-drop .swatch-yta { filter: drop-shadow(0 0 8px var(--acc)) brightness(.9); }
.f-drop { overflow: visible; }
.f-drop .swatch-yta {
filter: drop-shadow(0 0 8px var(--acc)) brightness(.9);
border-radius: 9px 9px 0 0;
}

/* Reglerbart filter: --lv (0–8) blir 0–16 px skuggradie. Labbet är sidans
scenografi och kopieras inte. */
Expand Down Expand Up @@ -5094,7 +5098,11 @@ <h3 class="demo-titel">filter: drop-shadow()</h3><ul class="stodrad" aria-label=
repeating-linear-gradient(90deg, rgb(0 0 0 / .22) 0 6px, transparent 6px 18px),
linear-gradient(120deg, var(--acc), #6ea8ff 60%, #ff6ab0);
}
.f-drop .swatch-yta { filter: drop-shadow(0 0 8px var(--acc)) brightness(.9); }</textarea></details>
.f-drop { overflow: visible; }
.f-drop .swatch-yta {
filter: drop-shadow(0 0 8px var(--acc)) brightness(.9);
border-radius: 9px 9px 0 0;
}</textarea></details>
<!-- demo-ctx:filter-drop-shadow --><p class="demo-ctx"><a class="ctx-kap" href="#farg">Kapitel 01 · Färg</a><span class="ctx-sep" aria-hidden="true">·</span><a class="ctx-reg" href="#reg-f">I registret: F</a></p><!-- /demo-ctx:filter-drop-shadow -->
</article>

Expand Down
24 changes: 21 additions & 3 deletions scripts/build.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,10 @@ const ordered = MIGRATED_IDS.map((id) => [id, sources.get(id)]);
`#${f.id}: begär swatch + swatch-solo + filter-motiv`);
assert(new RegExp(`class="swatch ${f.cls} ${MOTIF_CLASS}"`).test(src.markup),
`#${f.id}: markupen bär den delade motivklassen`);
assert(canon(src.css) === canon(`.${f.cls} .swatch-yta { filter: ${f.decl}; }`),
const expectedOwnCss = f.id === 'filter-drop-shadow'
? `.${f.cls} { overflow: visible; } .${f.cls} .swatch-yta { filter: ${f.decl}; border-radius: 9px 9px 0 0; }`
: `.${f.cls} .swatch-yta { filter: ${f.decl}; }`;
assert(canon(src.css) === canon(expectedOwnCss),
`#${f.id}: demots egen CSS är exakt den egna filter-deklarationen`);
assert(src.liveCss.includes(`.labbar.${f.cls}`) && src.liveCss.includes('var(--lv)'),
`#${f.id}: laboratoriets --lv-regel är live-only`);
Expand All @@ -701,7 +704,8 @@ const ordered = MIGRATED_IDS.map((id) => [id, sources.get(id)]);
`#${f.id}: kodvalvet bär varken sidscenografi eller främmande kapitel-CSS`);
const selectors = [...css.matchAll(/([^{}]+)\{/g)].map((m) => m[1].trim());
const allowed = ['.demo-yta', '.swatch', '.swatch-yta', '.swatch-lbl', '.swatch-solo',
'.swatch-solo > .swatch', '.swatch-solo .swatch-yta', `.${MOTIF_CLASS} .swatch-yta`, `.${f.cls} .swatch-yta`];
'.swatch-solo > .swatch', '.swatch-solo .swatch-yta', `.${MOTIF_CLASS} .swatch-yta`,
...(f.id === 'filter-drop-shadow' ? [`.${f.cls}`] : []), `.${f.cls} .swatch-yta`];
assert(selectors.length === allowed.length && selectors.every((s) => allowed.includes(s)),
`#${f.id}: exakt ägande av kodvalvets selektorer (${selectors.join(' ')})`);
for (const other of FILTERS.filter((o) => o.id !== f.id)) {
Expand Down Expand Up @@ -820,7 +824,11 @@ const ordered = MIGRATED_IDS.map((id) => [id, sources.get(id)]);
});
const FILTER_PATH = 'div:nth-child(5)>span:nth-child(1)';
assert(INTENTIONAL_DIFFS.length === 8 && INTENTIONAL_DIFFS.every((d) => d.path === FILTER_PATH),
'undantagslistan omfattar exakt åtta poster, alla på en och samma nodtyp');
'undantagslistan omfattar exakt åtta poster, alla med klassändringen på samma nodtyp');
const drop = INTENTIONAL_DIFFS.find((d) => d.id === 'filter-drop-shadow');
assert(drop.styles?.length === 2
&& drop.styles.map((s) => s.prop).join(',') === 'overflow,border-radius',
'drop-shadow-undantaget låser exakt overflow och ytan hörngeometri');
for (const d of INTENTIONAL_DIFFS) {
assert(MIGRATED_IDS.includes(d.id), `#${d.id}: undantaget gäller en migrerad demo`);
}
Expand All @@ -829,6 +837,16 @@ const ordered = MIGRATED_IDS.map((id) => [id, sources.get(id)]);
assert(notes.length === 1, `filter-blur: en avsiktlig skillnad normaliseras (${notes.length})`);
const untouched = applyIntentionalDiffs(fake('swatch grad-1'), fake('swatch grad-1'), 'linear-gradient');
assert(untouched.length === 0, 'linear-gradient: inget undantag, inget normaliseras');
const dropFake = (cls, overflow, radius) => ({
elements: [
{ ...fake(cls).elements[0], style: { overflow } },
{ path: 'div:nth-child(5)>span:nth-child(1)>span:nth-child(1)', style: { 'border-radius': radius } },
],
html: `<span class="${cls}"><span></span></span>`, controls: [], unstable: [],
});
const dropNotes = applyIntentionalDiffs(dropFake('swatch f-drop', 'hidden', '0px'),
dropFake('swatch f-drop f-motiv', 'visible', '9px 9px 0px 0px'), 'filter-drop-shadow');
assert(dropNotes.length === 3, 'drop-shadow: klass, overflow och hörngeometri normaliseras explicit');
const normalized = fake('swatch f-blur f-motiv');
applyIntentionalDiffs(fake('swatch f-blur'), normalized, 'filter-blur');
assert(normalized.elements[0].class === 'swatch f-blur'
Expand Down
18 changes: 17 additions & 1 deletion tests/demo-parity.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,10 @@ export const INTENTIONAL_DIFFS = [
{ id: 'filter-sepia', path: 'div:nth-child(5)>span:nth-child(1)', before: 'swatch f-sepia', after: 'swatch f-sepia f-motiv' },
{ id: 'filter-grayscale', path: 'div:nth-child(5)>span:nth-child(1)', before: 'swatch f-gray', after: 'swatch f-gray f-motiv' },
{ id: 'filter-invert', path: 'div:nth-child(5)>span:nth-child(1)', before: 'swatch f-invert', after: 'swatch f-invert f-motiv' },
{ id: 'filter-drop-shadow', path: 'div:nth-child(5)>span:nth-child(1)', before: 'swatch f-drop', after: 'swatch f-drop f-motiv' },
{ id: 'filter-drop-shadow', path: 'div:nth-child(5)>span:nth-child(1)', before: 'swatch f-drop', after: 'swatch f-drop f-motiv', styles: [
{ path: 'div:nth-child(5)>span:nth-child(1)', prop: 'overflow', before: 'hidden', after: 'visible' },
{ path: 'div:nth-child(5)>span:nth-child(1)>span:nth-child(1)', prop: 'border-radius', before: '0px', after: '9px 9px 0px 0px' },
] },
];

/**
Expand Down Expand Up @@ -398,6 +401,19 @@ export function applyIntentionalDiffs(baselineState, measuredState, id) {
measuredState.elements[i].class = d.before;
measuredState.html = measuredState.html.replace(`class="${d.after}"`, `class="${d.before}"`);
notes.push(`${d.path}: class "${d.before}" → "${d.after}" (avsiktlig gemensam motivklass)`);
for (const s of d.styles ?? []) {
const baselineEl = baselineState.elements.find((e) => e.path === s.path);
const measuredEl = measuredState.elements.find((e) => e.path === s.path);
if (!baselineEl || !measuredEl) throw new Error(`${id}: avsiktlig stilskillnad pekar på en nod som inte mäts (${s.path})`);
const before = baselineEl.style?.[s.prop];
const after = measuredEl.style?.[s.prop];
if (before !== s.before || after !== s.after) {
throw new Error(`${id} ${s.path} ${s.prop}: avsiktlig stilskillnad stämmer inte — baslinjen har "${before}" `
+ `(väntat "${s.before}"), dokumentet har "${after}" (väntat "${s.after}")`);
}
measuredEl.style[s.prop] = s.before;
notes.push(`${s.path}: ${s.prop} "${s.before}" → "${s.after}" (avsiktlig drop-shadow-geometri)`);
}
}
return notes;
}
Expand Down
23 changes: 15 additions & 8 deletions tests/demo-source.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -918,8 +918,14 @@ for (const f of FILTERS) {
const selectors = await snippetSelectors(t.page);
t.expect(selectors.includes(`.${f.cls} .swatch-yta`) && selectors.includes('.f-motiv .swatch-yta'),
`${f.id}: fristående kodvalv: egen filterregel och delat motiv`);
t.expect(selectors.filter((s) => /\.f-/.test(s)).length === 2,
`${f.id}: fristående kodvalv: bara det egna filtret och den delade motivklassen (${selectors.filter((s) => /\.f-/.test(s)).join(', ')})`);
const filterSelectors = selectors.filter((s) => /\.f-/.test(s)).sort();
const expectedFilterSelectors = [
'.f-motiv .swatch-yta',
`.${f.cls} .swatch-yta`,
...(f.id === 'filter-drop-shadow' ? ['.f-drop'] : []),
].sort();
t.expect(JSON.stringify(filterSelectors) === JSON.stringify(expectedFilterSelectors),
`${f.id}: fristående kodvalv: enbart delat motiv, eget filter${f.id === 'filter-drop-shadow' ? ' och egen overflow-korrigering' : ''} (${filterSelectors.join(', ')})`);
t.expect(!/\.(labbar|cm-row|rel-|gamut-|grad-|filter-row)/.test(selectors.join(' ')),
`${f.id}: fristående kodvalv: ingen laboratorie-, blandnings-, relativ-, gamut- eller gradient-CSS`);
// Det målade beviset: filtret måste synas i pixlarna på motivet.
Expand All @@ -932,12 +938,13 @@ for (const f of FILTERS) {
t.expect(ok, `${f.id}: fristående kodvalv: ${message}`);
t.expect(v.acc.startsWith('#'), `${f.id}: Grundpaketets accent används av motivet`);
if (f.id === 'filter-drop-shadow') {
// Skuggan klipps på sidan av .swatch { overflow: hidden } (oförändrat
// beteende). Utan klippningen målar drop-shadow() ändå en varm gloria:
// beviset att deklarationen inte är overksam.
const glow = await paintPair(t, f.cls, { clip: false });
t.expect(glow.ringFiltered.luma > 5 && glow.ringFiltered.warm > 5 && glow.ringReference.luma < 1,
`drop-shadow: fristående kodvalv: skuggan målar utanför formen (ljus ${glow.ringFiltered.luma}, värme ${glow.ringFiltered.warm} mot ${glow.ringReference.luma} ofiltrerat)`);
// Regression: .swatch { overflow: hidden } used to erase the entire
// outer shadow. The assertion samples the host screenshot outside the
// swatch-yta bounds, so a brighter interior alone cannot satisfy it.
t.expect(await t.style('.swatch', 'overflow') === 'visible',
'drop-shadow: fristående kodvalv: endast drop-shadow-swatchen öppnar overflow');
t.expect(m.ringFiltered.luma > 1 && m.ringFiltered.warm > 1 && m.ringReference.luma < 1,
`drop-shadow: fristående kodvalv: skuggan målar utanför formen (ljus ${m.ringFiltered.luma}, värme ${m.ringFiltered.warm} mot ${m.ringReference.luma} ofiltrerat)`);
}
},
};
Expand Down
Loading