Skip to content
Draft
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
17 changes: 17 additions & 0 deletions apps/prs/angular/src/dark-mode-overrides.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* DARK MODE + V2 TOKEN OVERRIDES
*
* Load order matters:
* 1. @abgov/style (imported in app.tsx) loads V1 tokens
* 2. design-tokens-v2 overrides with V2 values
* 3. surface-tokens.css defines the surface elevation system
* 4. dark-theme.css overrides for dark mode
*
* To disable: comment out the import of this file in app.tsx
*
* To toggle dark mode, use the moon/sun icon at the bottom of the side menu.
*/

@import "@abgov/design-tokens-v2/dist/tokens.css";
@import "./surface-tokens.css";
@import "./dark-theme.css";
519 changes: 519 additions & 0 deletions apps/prs/angular/src/dark-theme.css

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<goab-block gap="l" direction="column">
<goab-text tag="h1" size="heading-l">
Feature 3873 &mdash; GoabThemeService demo
</goab-text>
<goab-text tag="p" size="body-m">
This page demonstrates the Angular theme service shipped from
<code>&#64;abgov/angular-components</code>. No manual
<code>document.documentElement.setAttribute</code> in this component &mdash;
the service owns all DOM and localStorage side effects.
</goab-text>

<goab-callout type="information" heading="Current mode">
<goab-text tag="p" size="body-m">
The service exposes a signal. Current value: <strong>{{ theme.mode() }}</strong>.
Re-render is tracked automatically by Angular when the signal changes.
</goab-text>
</goab-callout>

<goab-container type="interactive" padding="relaxed">
<goab-text tag="h2" size="heading-m">Controls</goab-text>
<goab-block gap="m" direction="row">
<goab-button type="primary" (onClick)="toggle()">
Toggle ({{ theme.mode() === 'light' ? 'to dark' : 'to light' }})
</goab-button>
<goab-button
type="secondary"
[disabled]="theme.mode() === 'light'"
(onClick)="setLight()"
>
Force light
</goab-button>
<goab-button
type="secondary"
[disabled]="theme.mode() === 'dark'"
(onClick)="setDark()"
>
Force dark
</goab-button>
</goab-block>
</goab-container>

<goab-container type="interactive" padding="relaxed">
<goab-text tag="h2" size="heading-m">How to consume</goab-text>
<goab-text tag="p" size="body-m">
Inject the service, bind to the signal in the template:
</goab-text>
<pre style="overflow-x: auto">
theme = inject(GoabThemeService);
// template: &#123;&#123; theme.mode() &#125;&#125;
// call: theme.toggle() / theme.setMode('dark')
</pre>
</goab-container>

<goab-container type="info" padding="relaxed">
<goab-text tag="h2" size="heading-m">What the service does</goab-text>
<goab-text tag="p" size="body-m">
On first inject, reads initial mode from: localStorage &rarr; existing
<code>data-theme</code> attribute &rarr; system preference
(<code>prefers-color-scheme</code>) &rarr; default light.
</goab-text>
<goab-text tag="p" size="body-m">
On every change, an <code>effect()</code> syncs
<code>data-theme</code> on <code>&lt;html&gt;</code> and persists to
localStorage. Refresh the page &mdash; mode persists.
</goab-text>
</goab-container>
</goab-block>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Component, CUSTOM_ELEMENTS_SCHEMA, inject } from "@angular/core";
import {
GoabBlock,
GoabButton,
GoabCallout,
GoabContainer,
GoabText,
GoabThemeService,
} from "@abgov/angular-components";

@Component({
standalone: true,
selector: "abgov-feat3873",
templateUrl: "./feat3873.component.html",
imports: [GoabBlock, GoabButton, GoabCallout, GoabContainer, GoabText],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class Feat3873Component {
readonly theme = inject(GoabThemeService);

setLight(): void {
this.theme.setMode("light");
}

setDark(): void {
this.theme.setMode("dark");
}

toggle(): void {
this.theme.toggle();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"title": "GoabThemeService demo",
"path": "features/3873",
"id": "3873",
"type": "feature"
}
3 changes: 3 additions & 0 deletions apps/prs/angular/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
@import "../../../../dist/libs/web-components/index.css";
@import "@abgov/design-tokens-v2/dist/tokens.css";

/* Dark mode spike: loads surface tokens + dark theme overrides in guaranteed order. */
@import "./dark-mode-overrides.css";

:root {
--goa-space-fill: 32ch;
}
75 changes: 75 additions & 0 deletions apps/prs/angular/src/surface-tokens.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* ============================================================================
SURFACE TOKENS - Two-layer elevation system
============================================================================
Layer 1: Scale (numbered, the palette)
Layer 2: Semantic (named, reference the scale)

In light mode, most surfaces resolve to white or near-white because
shadows handle depth. In dark mode, lighter surfaces = higher elevation.
============================================================================ */

/* Light mode defaults */
:root {
/* Scale tokens */
--goa-color-surface-0: var(--goa-color-greyscale-50); /* #f8f8f8 */
--goa-color-surface-50: var(--goa-color-greyscale-white); /* #ffffff */
--goa-color-surface-100: var(--goa-color-greyscale-white); /* #ffffff */
--goa-color-surface-150: var(--goa-color-greyscale-50); /* #f8f8f8 */
--goa-color-surface-200: var(--goa-color-greyscale-50); /* #f8f8f8 */
--goa-color-surface-250: var(--goa-color-greyscale-100); /* #f2f0f0 */
--goa-color-surface-300: var(--goa-color-greyscale-white); /* #ffffff */
--goa-color-surface-350: var(--goa-color-greyscale-100); /* #f2f0f0 */
--goa-color-surface-400: var(--goa-color-greyscale-50); /* #f8f8f8 */

/* Semantic tokens - primary surfaces */
--goa-color-surface-page: var(--goa-color-surface-0);
--goa-color-surface-default: var(--goa-color-surface-50);
--goa-color-surface-card: var(--goa-color-surface-100);
--goa-color-surface-section: var(--goa-color-surface-150);
--goa-color-surface-input: var(--goa-color-surface-200);
--goa-color-surface-content-body: var(--goa-color-surface-250);
--goa-color-surface-widget: var(--goa-color-surface-300);
--goa-color-surface-heading: var(--goa-color-surface-350);
--goa-color-surface-item: var(--goa-color-surface-400);

/* Semantic tokens - additional surfaces */
--goa-color-surface-table-data: var(--goa-color-greyscale-white);
--goa-color-surface-container-heading: var(--goa-color-greyscale-100);
--goa-color-surface-popover: var(--goa-color-greyscale-white);
--goa-color-surface-app-header: var(--goa-color-greyscale-white);

/* Hover tokens */
--goa-color-surface-card-hover: var(--goa-color-greyscale-50);
--goa-color-surface-widget-hover: var(--goa-color-greyscale-50);
--goa-color-surface-heading-hover: var(--goa-color-greyscale-150);
--goa-color-surface-item-hover: var(--goa-color-greyscale-100);

/* Static white - never flips, for white pigment on colored backgrounds */
--goa-color-static-white: #ffffff;
}

/* Dark mode overrides */
:root[data-theme="dark"] {
/* Scale */
--goa-color-surface-0: #1a1a1a;
--goa-color-surface-50: #1e1e1e;
--goa-color-surface-100: #232323;
--goa-color-surface-150: #252525;
--goa-color-surface-200: #262626;
--goa-color-surface-250: #292929;
--goa-color-surface-300: #2e2e2e;
--goa-color-surface-350: #333333;
--goa-color-surface-400: #383838;

/* Additional surfaces - independent values tuned for dark mode */
--goa-color-surface-table-data: #2c2c2c;
--goa-color-surface-container-heading: #353535;
--goa-color-surface-popover: #242424;
--goa-color-surface-app-header: #222222;

/* Hover tokens */
--goa-color-surface-card-hover: #2e2e2e;
--goa-color-surface-widget-hover: #3a3a3a;
--goa-color-surface-heading-hover: #3f3f3f;
--goa-color-surface-item-hover: #444444;
}
37 changes: 34 additions & 3 deletions apps/prs/react/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { CSSProperties } from "react";
import { type CSSProperties } from "react";
import { Outlet, useNavigate } from "react-router-dom";
import {
GoabAppFooter,
GoabAppHeader,
GoabMicrositeHeader,
GoabOneColumnLayout,
GoabThemeProvider,
GoabWorkSideMenu,
GoabWorkSideMenuGroup,
GoabWorkSideMenuItem,
useTheme,
} from "@abgov/react-components";
import {
bugRouteDefinitions,
Expand All @@ -16,14 +18,22 @@ import {
} from "./route-manifest";
import "@abgov/design-tokens-v2/dist/tokens.css"; // Production tokens. Comment out to test with legacy V1 token values.

import "@abgov/style";

// Dark mode spike: loads V2 tokens + surface tokens + dark theme overrides in guaranteed order.
// Comment out to disable dark mode entirely.
import "../dark-mode-overrides.css";

const appContentStyle: CSSProperties = {
display: "flex",
minHeight: "calc(100vh - 10.1875rem)",
"--goa-work-side-menu-height": "calc(100vh - 10.1875rem)",
} as CSSProperties;

export function App() {
function AppShell() {
const navigate = useNavigate();
const { mode, toggle } = useTheme();
const isDark = mode === "dark";

return (
<GoabOneColumnLayout>
Expand All @@ -36,7 +46,13 @@ export function App() {
heading="Testing Playground"
url="/"
open={true}
onNavigate={(path: string) => navigate(path)}
onNavigate={(path: string) => {
if (path === "#toggle-theme") {
toggle();
} else {
navigate(path);
}
}}
primaryContent={
<>
<GoabWorkSideMenuGroup icon="alert-circle" heading="Bugs">
Expand Down Expand Up @@ -70,6 +86,13 @@ export function App() {
<GoabWorkSideMenuItem icon="list" label="Everything" url="/everything" />
</>
}
secondaryContent={
<GoabWorkSideMenuItem
icon={isDark ? "sunny" : "moon"}
label={isDark ? "Light mode" : "Dark mode"}
url="#toggle-theme"
/>
}
/>
<section style={{ padding: "30px", width: "100%" }} role="main">
<Outlet />
Expand All @@ -82,4 +105,12 @@ export function App() {
);
}

export function App() {
return (
<GoabThemeProvider>
<AppShell />
</GoabThemeProvider>
);
}

export default App;
17 changes: 17 additions & 0 deletions apps/prs/react/src/dark-mode-overrides.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* DARK MODE + V2 TOKEN OVERRIDES
*
* Load order matters:
* 1. @abgov/style (imported in app.tsx) loads V1 tokens
* 2. design-tokens-v2 overrides with V2 values
* 3. surface-tokens.css defines the surface elevation system
* 4. dark-theme.css overrides for dark mode
*
* To disable: comment out the import of this file in app.tsx
*
* To toggle dark mode, use the moon/sun icon at the bottom of the side menu.
*/

@import "@abgov/design-tokens-v2/dist/tokens.css";
@import "./surface-tokens.css";
@import "./dark-theme.css";
Loading
Loading