diff --git a/e2e/pageobjects/canvas.page.ts b/e2e/pageobjects/canvas.page.ts index ca5d60e..d235f94 100644 --- a/e2e/pageobjects/canvas.page.ts +++ b/e2e/pageobjects/canvas.page.ts @@ -207,6 +207,22 @@ export const canvas = { ); }, + /** + * The colour of the hairline around one format's badge in the rail. ⚠️ Read on the badge + * and not on the chip: the chip is the click surface, the badge is what carries the ring, + * and they live in two different components — which is the whole trap (#207). + */ + badgeRing(language: string): Promise { + return browser.execute( + (chipSelector: string, wanted: string) => { + const chip = document.querySelector(`${chipSelector}[data-language="${wanted}"]`); + const badge = chip?.querySelector('.lang-tag'); + return badge ? getComputedStyle(badge).borderTopColor : null; + }, + testid('language-chip'), + language, + ); + }, async waitForCard(title: string): Promise { await browser.waitUntil(async () => (await canvas.titles()).includes(title), { timeout: 15_000, diff --git a/e2e/specs/06-search-and-filters.e2e.ts b/e2e/specs/06-search-and-filters.e2e.ts index 47fbdf6..c0d3a8f 100644 --- a/e2e/specs/06-search-and-filters.e2e.ts +++ b/e2e/specs/06-search-and-filters.e2e.ts @@ -145,6 +145,21 @@ describe('Search, filters and facets', () => { await canvas.toggleLanguage('sh'); }); + /** + * ⚠️ #207: the rail cannot reach the badge. `.lang-tag` belongs to `app-language-badge`, + * so a rule written in the rail's own stylesheet is rewritten with the rail's + * `_ngcontent` attribute and never matches — the selection had no mark at all while + * `outlines` above still counted one. The badge's own hairline was that one. + */ + it('marks the selected format on the badge that carries the ring', async () => { + const resting = await canvas.badgeRing('sh'); + await canvas.toggleLanguage('sh'); + const selected = await canvas.badgeRing('sh'); + await canvas.toggleLanguage('sh'); + + expect(resting).not.toBeNull(); + expect(selected).not.toBe(resting); + }); it('drops the search, the tag and the language in one click', async () => { await canvas.search('Docker'); await canvas.toggleTag('ops'); diff --git a/src/app/notes/header/language-rail/language-rail.component.scss b/src/app/notes/header/language-rail/language-rail.component.scss index dd1be54..4dc8e1d 100644 --- a/src/app/notes/header/language-rail/language-rail.component.scss +++ b/src/app/notes/header/language-rail/language-rail.component.scss @@ -35,13 +35,18 @@ // #196, and a second outline two pixels further out — at a different radius — read as two // rings around one badge rather than as a selection. The padding stays, because it is what // the chip is clickable by. -.language-chip:hover .lang-tag { - border-color: var(--line); +// +// ⚠️ Which is why the state is handed down as a custom property and not written as a rule +// on `.lang-tag`: that element belongs to `app-language-badge`, and emulated encapsulation +// rewrites a selector spelled here with *this* component's `_ngcontent` attribute. It then +// matches nothing, silently — and the selected format had no mark at all (#207). +.language-chip:hover { + --badge-ring: var(--line); } -.language-chip.on .lang-tag { - border-color: var(--amber); - background: var(--bg-2); +.language-chip.on { + --badge-ring: var(--amber); + --badge-surface: var(--bg-2); } .language-chip:focus-visible { diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss index bfd615a..ad090e8 100644 --- a/src/styles/_mixins.scss +++ b/src/styles/_mixins.scss @@ -188,9 +188,11 @@ // ⚠️ The hairline is what gives the chip a shape, and it is in the hue rather than in a // grey: `--bg-3` sits at 1.1:1 against the light theme's surfaces, so a neutral fill alone // left the badge as bare text on every light screen. +// ⚠️ `--badge-ring` and `--badge-surface` are the only way in from outside: the badge is a +// component of its own, so a consumer's stylesheet cannot reach `.lang-tag` with a rule. @mixin hue-badge($colour) { - background: var(--bg-3); - border: 1px solid color-mix(in srgb, #{$colour} 40%, transparent); + background: var(--badge-surface, var(--bg-3)); + border: 1px solid var(--badge-ring, color-mix(in srgb, #{$colour} 40%, transparent)); color: $colour; }