-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement dark mode #9181
Merged
Merged
Implement dark mode #9181
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
91c9b6a
Added an example of a new palette architecture
JamieB-gu 5a069cd
Lint fix
JamieB-gu 1133034
Updated decorator to take `format`
JamieB-gu b13a7fb
add palette declarations in article page for light and dark modes
cemms1 6ccd3c8
add condition of switch from frontend for dark mode in apps
cemms1 9b48ee9
docs: add comments to palette declarations in global css block
cemms1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
dotcom-rendering/src/components/HeadlineExample.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// ----- Imports ----- // | ||
|
||
import { css } from '@emotion/react'; | ||
import { ArticleDesign, ArticleDisplay, ArticlePillar } from '@guardian/libs'; | ||
import type { Decorator, Meta, StoryObj } from '@storybook/react'; | ||
import { paletteDeclarations } from '../palette'; | ||
import { HeadlineExample } from './HeadlineExample'; | ||
|
||
// ----- Meta ----- // | ||
|
||
const meta: Meta<typeof HeadlineExample> = { | ||
title: 'components/HeadlineExample', | ||
component: HeadlineExample, | ||
}; | ||
|
||
export default meta; | ||
|
||
// ----- Decorators ----- // | ||
|
||
/** | ||
* Creates storybook decorator used to wrap components in an element | ||
* containing the light or dark mode palette colours. | ||
* | ||
* @param colourScheme Choose whether to use the light or darPerformanceNavigation.type palette. | ||
* @returns A decorator that wraps the component in a `div` containing the | ||
* palette colours as CSS custom properties. | ||
*/ | ||
const colourSchemeDecorator = | ||
(colourScheme: 'light' | 'dark') => | ||
(format: ArticleFormat): Decorator => | ||
(Story) => | ||
( | ||
<div css={css(paletteDeclarations(format, colourScheme))}> | ||
<Story /> | ||
</div> | ||
); | ||
|
||
const lightMode = colourSchemeDecorator('light'); | ||
const darkMode = colourSchemeDecorator('dark'); | ||
|
||
// ----- Stories ----- // | ||
|
||
type Story = StoryObj<typeof HeadlineExample>; | ||
|
||
const articleFormat: ArticleFormat = { | ||
design: ArticleDesign.Standard, | ||
display: ArticleDisplay.Standard, | ||
theme: ArticlePillar.News, | ||
}; | ||
|
||
export const LightHeadline: Story = { | ||
args: { | ||
text: 'A short example headline', | ||
}, | ||
decorators: [lightMode(articleFormat)], | ||
}; | ||
|
||
export const DarkHeadline: Story = { | ||
args: LightHeadline.args, | ||
decorators: [darkMode(articleFormat)], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// ----- Imports ----- // | ||
|
||
import { css } from '@emotion/react'; | ||
import { headline } from '@guardian/source-foundations'; | ||
import { palette } from '../palette'; | ||
|
||
// ----- Component ----- // | ||
|
||
export const HeadlineExample = ({ text }: { text: string }) => { | ||
const styles = css` | ||
color: ${palette('--headline-colour')}; | ||
background-color: ${palette('--headline-background-colour')}; | ||
${headline.large()} | ||
`; | ||
return <h1 css={styles}>{text}</h1>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
// ----- Imports ----- // | ||
|
||
import { ArticleDesign, type ArticleFormat } from '@guardian/libs'; | ||
import { palette as sourcePalette } from '@guardian/source-foundations'; | ||
|
||
// ----- Palette Functions ----- // | ||
|
||
const headlineColourLight = ({ design }: ArticleFormat): string => { | ||
switch (design) { | ||
case ArticleDesign.Feature: | ||
return sourcePalette.news[300]; | ||
default: | ||
return sourcePalette.neutral[10]; | ||
} | ||
}; | ||
|
||
const headlineColourDark = ({ design }: ArticleFormat): string => { | ||
switch (design) { | ||
case ArticleDesign.Feature: | ||
return sourcePalette.news[600]; | ||
default: | ||
return sourcePalette.neutral[97]; | ||
} | ||
}; | ||
|
||
const headlineBackgroundColourLight = ({ design }: ArticleFormat): string => { | ||
switch (design) { | ||
case ArticleDesign.LiveBlog: | ||
return sourcePalette.news[400]; | ||
default: | ||
return sourcePalette.neutral[100]; | ||
} | ||
}; | ||
|
||
const headlineBackgroundColourDark = ({ design }: ArticleFormat): string => { | ||
switch (design) { | ||
case ArticleDesign.LiveBlog: | ||
return sourcePalette.news[200]; | ||
default: | ||
return sourcePalette.neutral[7]; | ||
} | ||
}; | ||
|
||
// ----- Palette ----- // | ||
|
||
/** | ||
* A template literal type used to make sure the keys of the palette use the | ||
* correct CSS custom property syntax. | ||
*/ | ||
type CSSCustomProperty = `--${string}`; | ||
/** | ||
* Ensures that all palette functions provide the same API, deriving a palette | ||
* colour from an {@linkcode ArticleFormat}. | ||
*/ | ||
type PaletteFunction = (f: ArticleFormat) => string; | ||
/** | ||
* Used to validate that the palette object always has the correct shape, | ||
* without changing its type. | ||
*/ | ||
type PaletteColours = Record< | ||
CSSCustomProperty, | ||
{ | ||
light: PaletteFunction; | ||
dark: PaletteFunction; | ||
} | ||
>; | ||
|
||
/** | ||
* Maps palette colour names (which are also CSS custom property names) to | ||
* a pair of palette functions, which can be used to derive both light and dark | ||
* mode colours from an {@linkcode ArticleFormat}. | ||
* | ||
* This is not accessed directly in components; the {@linkcode palette} function | ||
* is used instead. | ||
*/ | ||
const paletteColours = { | ||
'--headline-colour': { | ||
light: headlineColourLight, | ||
dark: headlineColourDark, | ||
}, | ||
'--headline-background-colour': { | ||
light: headlineBackgroundColourLight, | ||
dark: headlineBackgroundColourDark, | ||
}, | ||
} satisfies PaletteColours; | ||
|
||
/** | ||
* A union of all the keys of the palette object. In other words, all the | ||
* possible colours that can be chosen. | ||
*/ | ||
type ColourName = keyof typeof paletteColours; | ||
|
||
/** | ||
* Looks up a palette colour by name. Retrieves a CSS value for the specified | ||
* colour, for use in CSS declarations. See the examples for how this is | ||
* commonly used with our Emotion-based styles. | ||
* | ||
* @param a The name of a palette colour; for example `--headline-colour`. | ||
* @returns A CSS `var` function call; for example `var(--headline-colour)`. | ||
* @example | ||
* const styles = css` | ||
* color: ${palette('--headline-colour')}; | ||
* background-color: ${palette('--headline-background-colour')}; | ||
* `; | ||
*/ | ||
const palette = (colour: ColourName): string => `var(${colour})`; | ||
|
||
/** | ||
* Builds a list of CSS custom property declarations representing colours. These | ||
* can be used to set up the palette on any element, and then retrieved to apply | ||
* styles via the {@linkcode palette} function. See the examples for ways the | ||
* palette could be set up. | ||
* | ||
* @param format The `ArticleFormat` of the current article. | ||
* @param colourScheme Get declarations for either `light` or `dark` mode. | ||
* @returns A set of CSS custom property declarations for palette colours, | ||
* in string format. For example: | ||
* ``` | ||
* [ '--headline-colour: #1a1a1a;', '--headline-background-colour: #ffffff;' ] | ||
* ``` | ||
* @example | ||
* <caption>Create a single stylesheet to handle both colour schemes.</caption> | ||
* const paletteStyles = css` | ||
* :root { | ||
* ${paletteDeclarations(format, 'light').join('\n')} | ||
* } | ||
* | ||
* (@)media (prefers-color-scheme: dark) { | ||
* :root { | ||
* ${paletteDeclarations(format, 'dark').join('\n')} | ||
* } | ||
* } | ||
* `; | ||
* @example | ||
* <caption>Load separate stylesheets based on user preference.</caption> | ||
* // Use to build a file called 'light.css'. | ||
* const lightPalette = css` | ||
* :root { | ||
* ${paletteDeclarations(format, 'light').join('\n')} | ||
* } | ||
* `; | ||
* // Use to build a file called 'dark.css'. | ||
* const darkPalette = css` | ||
* :root { | ||
* ${paletteDeclarations(format, 'dark').join('\n')} | ||
* } | ||
* `; | ||
* | ||
* const stylesheets = ( | ||
* <> | ||
* <link | ||
* media="(prefers-color-scheme: light)" | ||
* rel="stylesheet" | ||
* href="light.css" | ||
* /> | ||
* <link | ||
* media="(prefers-color-scheme: dark)" | ||
* rel="stylesheet" | ||
* href="dark.css" | ||
* /> | ||
* </> | ||
* ); | ||
*/ | ||
const paletteDeclarations = ( | ||
format: ArticleFormat, | ||
colourScheme: 'light' | 'dark', | ||
): string[] => | ||
Object.entries(paletteColours).map( | ||
([colourName, colour]) => | ||
`${colourName}: ${colour[colourScheme](format)};`, | ||
); | ||
|
||
// ----- Exports ----- // | ||
|
||
export { palette, paletteDeclarations }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love
PerformanceNavigation.type
as much as the next person, but not sure what it’s doing there…There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how are you SO eagle eyed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great spot