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
10 changes: 9 additions & 1 deletion .github/astro.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,15 @@ Sidebar topics are defined in `config/sidebar/` as separate modules and aggregat

## Cookie Consent

The site uses Microsoft's **WCP** consent runtime (`wcp-consent.js`, loaded live from `wcpstatic.microsoft.com`), wired up in `src/components/starlight/Head.astro` and fully restyled by `src/styles/wcp-consent.css`. The banner is **geo-gated**: the CDN decides per region whether it appears, so where consent is not required (e.g. the US) **no banner shows** and the "Manage cookies" buttons are hidden (`html[data-consent-not-required]`). When it does appear (e.g. the EU) it is a **fixed strip at the top of the viewport** — not a bottom-right box — with **"Accept"**, **"Reject"**, and **"More info"** actions; "More info" opens a preferences dialog with per-category toggles and "Save changes" / "Reset all". Consent is persisted in the `MSCC` cookie. Because it is geo-gated, you usually will not see it in local/US automation — if it does appear, dismiss it first.
The site uses Microsoft's **WCP** consent runtime (`wcp-consent.js`, loaded live from `wcpstatic.microsoft.com`), wired up in `src/components/starlight/Head.astro` and fully restyled by `src/styles/wcp-consent.css`. The banner is **geo-gated**: the CDN decides per region whether it appears, so where consent is not required (e.g. the US) **no banner shows** and the "Manage cookies" buttons are hidden (`html[data-consent-not-required]`). When it does appear (e.g. the EU) it is a **fixed strip at the top of the viewport** — not a bottom-right box — with **"Accept"**, **"Reject"**, and **"Manage cookies"** actions; "Manage cookies" opens a preferences dialog with per-category toggles and "Save changes" / "Reset all". Consent is persisted in the `MSCC` cookie. Because it is geo-gated, you usually will not see it in local/US automation — if it does appear, dismiss it first unless you are inspecting the consent UI itself.

Cookie management is also available as a text-style **Manage Cookies** action under **Legal** on all viewport sizes where WCP requires consent. It replaces the mobile footer cookie icon; the desktop header icon remains. The Legal action is server-rendered with the unique ID `c-uhff-footer_managecookies` and uses the existing delegated `[data-cookie-manage-consent]` handler.

The banner's **Accept** and dialog's **Save changes** buttons use the shared `--aspire-action-primary`, `--aspire-action-primary-text`, and `--aspire-action-primary-hover` tokens, matching **Try Aspire** in both themes. Do not use `--aspire-color-light` for primary action backgrounds: it aliases muted text and becomes gray in light mode.

For scanners, use `//*[@id="c-uhff-footer_managecookies"]` after deploying this markup. Do not use `//button[contains(@class, 'cookie-consent-btn')]`: it matches multiple controls, including hidden responsive variants. Verify exactly one match and an ordinary click that reopens preferences after a saved choice. WCP must have initialized in a consent-required region; a hidden control in a non-consent region or a blocked WCP CDN is not fixed by a different XPath. Confirm the scanned deployment and rerun the external scan rather than treating local tests as compliance certification.

Complete the initial banner choice before testing the footer's reopening action, especially on narrow screens where the banner can cover most of the viewport. Use the banner's own management action to inspect preferences before making an initial choice.

## Screenshots and Visual Verification with playwright-cli

Expand Down
41 changes: 38 additions & 3 deletions src/frontend/src/components/FooterLegal.astro
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ const t = Astro.locals.t as unknown as (key: string) => string;
{t('footer.privacyAndCookies')}
</a>
</li>
<li class="cookie-consent-btn">
<button
id="c-uhff-footer_managecookies"
type="button"
class="cookie-consent-btn"
data-cookie-manage-consent
data-tour-target="cookie-preferences"
data-tour-step="cookie-preferences"
aria-haspopup="dialog"
>
{t('footer.manageCookies')}
</button>
</li>
<li>
<a
href="https://go.microsoft.com/fwlink/?LinkID=206977"
Expand Down Expand Up @@ -78,7 +91,8 @@ const t = Astro.locals.t as unknown as (key: string) => string;
gap: 0.4rem;
}

.footer-links a {
.footer-links a,
.footer-links button {
display: inline-flex;
align-items: center;
gap: 0.4rem;
Expand All @@ -87,16 +101,37 @@ const t = Astro.locals.t as unknown as (key: string) => string;
font-size: var(--sl-text-sm);
}

.footer-links a:hover {
.footer-links button {
width: fit-content;
padding: 0;
border: 0;
background: none;
font-family: inherit;
font-size: var(--sl-text-xs);
line-height: 1.45;
text-align: start;
cursor: pointer;
min-height: 1.5rem;
}
Comment thread
Copilot marked this conversation as resolved.

.footer-links a:hover,
.footer-links button:hover {
color: var(--sl-color-white);
}

.footer-links button:focus-visible {
outline: 2px solid var(--sl-color-text-accent);
outline-offset: 0.15rem;
border-radius: 0.15rem;
}

.privacy-choice {
gap: 0.5rem;
}

@media (prefers-reduced-motion: no-preference) {
.footer-links a {
.footer-links a,
.footer-links button {
transition: color 150ms ease;
}
}
Expand Down
66 changes: 7 additions & 59 deletions src/frontend/src/components/FooterSocials.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
---
import CookiesSvg from '@assets/icons/cookies.svg';
import { Icon } from '@astrojs/starlight/components';
import { socialConfig, type SocialLink } from '../../config/socials.config';

Expand Down Expand Up @@ -56,22 +55,6 @@ const opensInNewTab = Astro.locals.t('footer.opensInNewTab' as any);
))
}
</ul>

<div class="footer-mobile-tools" role="group" aria-label="Site tools">
<button
type="button"
class="footer-mobile-tool-btn cookie-consent-btn"
data-cookie-manage-consent
data-tour-target="cookie-preferences"
data-tour-step="cookie-preferences"
data-placement="top"
aria-label="Open cookie preferences dialog"
title="Cookies"
>
<CookiesSvg />
<span class="footer-mobile-tool-label">Cookies</span>
</button>
</div>
</div>
</section>

Expand All @@ -92,8 +75,7 @@ const opensInNewTab = Astro.locals.t('footer.opensInNewTab' as any);
}

.social-actions,
.social-links,
.footer-mobile-tools {
.social-links {
display: flex;
flex-wrap: wrap;
gap: 0.3rem;
Expand All @@ -109,8 +91,7 @@ const opensInNewTab = Astro.locals.t('footer.opensInNewTab' as any);
list-style: none;
}

.social-links a,
.footer-mobile-tool-btn {
.social-links a {
width: 2rem;
height: 2rem;
display: grid;
Expand All @@ -127,21 +108,17 @@ const opensInNewTab = Astro.locals.t('footer.opensInNewTab' as any);
}

.social-links a:hover,
.social-links a:focus-visible,
.footer-mobile-tool-btn:hover,
.footer-mobile-tool-btn:focus-visible {
.social-links a:focus-visible {
color: var(--sl-color-white);
background: color-mix(in srgb, var(--sl-color-gray-5) 56%, transparent);
}

.social-links a:focus-visible,
.footer-mobile-tool-btn:focus-visible {
.social-links a:focus-visible {
outline: 2px solid var(--sl-color-text-accent);
outline-offset: 0.15rem;
}

.social-links a :global(svg),
.footer-mobile-tool-btn :global(svg) {
.social-links a :global(svg) {
--sl-icon-size: 1rem !important;
width: 1rem;
height: 1rem;
Expand All @@ -152,16 +129,6 @@ const opensInNewTab = Astro.locals.t('footer.opensInNewTab' as any);
display: none;
}

.footer-mobile-tools {
display: none;
}

.footer-mobile-tool-label {
font-size: var(--sl-text-2xs);
font-weight: 700;
line-height: 1;
}

.sr-only {
position: absolute;
width: 1px;
Expand All @@ -173,35 +140,16 @@ const opensInNewTab = Astro.locals.t('footer.opensInNewTab' as any);
border: 0;
}

@media (max-width: 49.999rem) {
.footer-mobile-tools {
display: flex;
flex: 1 0 100%;
margin-top: 0.25rem;
}

.footer-mobile-tool-btn {
width: auto;
min-width: 3.5rem;
display: inline-flex;
gap: 0.35rem;
padding-inline: 0.55rem;
border-color: color-mix(in srgb, var(--sl-color-gray-5) 72%, transparent);
}
}

@media (prefers-reduced-motion: no-preference) {
.social-links a,
.footer-mobile-tool-btn {
.social-links a {
transition:
color 160ms ease,
background-color 160ms ease;
}
}

@media (forced-colors: active) {
.social-links a,
.footer-mobile-tool-btn {
.social-links a {
border-color: CanvasText;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/components/starlight/Head.astro
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ function computeSourceUrl() {
if (window.__aspireWcpConsentInitialized) return;
window.__aspireWcpConsentInitialized = true;

// The four "Manage cookies" buttons are always server-rendered. Once WCP
// Cookie controls, including the Legal text action, are server-rendered. Once WCP
// tells us whether consent is required for this region, reflect it on <html>
// so CSS can hide those buttons where there is nothing to manage (e.g. the
// US). Persist the answer so a returning visitor doesn't see the button
Expand Down
1 change: 1 addition & 0 deletions src/frontend/src/content/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@
"reference": "Reference",
"contributorGuide": "Contributor guide",
"privacyAndCookies": "Privacy & Cookies",
"manageCookies": "Manage Cookies",
"terms": "Terms of Use",
"trademarks": "Trademarks",
"translatePage": "Translate this page",
Expand Down
Loading
Loading