Skip to content

feat(tokens): integrate light-dark #3672

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

Draft
wants to merge 2 commits into
base: spectrum-two
Choose a base branch
from
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
91 changes: 59 additions & 32 deletions .github/QUICK-START.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,88 @@ Install the components you want along with their dependencies. Here's an example
yarn add -DW @spectrum-css/tokens @spectrum-css/typography @spectrum-css/page @spectrum-css/icon @spectrum-css/button
```

Spectrum CSS components utilize custom properties in order to change themes and scales. For these to apply, a couple of classes need to be added to the document’s `<html>` tag based on the [visual language](https://github.com/adobe/spectrum-css?tab=readme-ov-file#visual-language), [scale](https://github.com/adobe/spectrum-css?tab=readme-ov-file#scales), and [theme](https://github.com/adobe/spectrum-css?tab=readme-ov-file#themes-colorstops) you wish to use. For example, the following code snippet will display styling for the default Spectrum visual language using medium scale and light color theme:
Spectrum CSS components utilize custom properties in order to express our design language through a set of core tokens. We leverage the `@adobe/spectrum-tokens` data as a source of this design data and convert it into a set of CSS custom properties. This allows us to use the tokens in our components and to create a consistent design language across all of our components.

Some of these tokens have different values depending on the visual language or scale being used. The default values for all tokens are set to the default values for the light theme and medium scale.

To force the dark theme, you can add `color-scheme: dark` to your container element. Doing this will force the dark value to be used for all tokens that have one. This can be done at any level of the DOM and by leveraging the cascade, the color schemes can be nested or changed at any level. For example, if you want to force the dark theme for a specific component, you can add `color-scheme: dark` to that component's container element.

```html
<html class="spectrum spectrum--medium spectrum--light"></html>
<style>
:root {
/* Allow user preference to control the color scheme at first */
color-scheme: light dark;
}
</style>
<div class="container" style="color-scheme: dark">
<p>A dark themed container</p>
<div class="container" style="color-scheme: light">
<p>A light themed container</p>
</div>
</div>
```

The design language also includes a set of token values that represent different device sizes. At the moment, these values are only defined as "medium" and "large", with "medium" as the default which maps generally to a desktop or laptop screen. The "large" value is intended for smaller devices, such as phones and tablets. The default value for all tokens is set to the default value for the medium scale. To force the large scale, you can update the cascading layers inheritance:

```css
@layers defaults, medium;

@media screen and (min-width: 768px) {
@layers defaults, large;
}
```

What's happening here is that the `defaults` layer is being overridden by the `large` layer when the screen size is greater than 768px. This means that all tokens that have a value for the `large` scale will be used instead of the default value. The most useful feature of this approach is that each application can make their own decision about which scale to leverage and at what screen size. This allows for a lot of flexibility in how the design language is applied to different applications.

Use the `index.css` files in your project to include component and global styles ([background theme/colorstop](https://github.com/adobe/spectrum-css?tab=readme-ov-file#themes-colorstops), [platform scaling](https://github.com/adobe/spectrum-css?tab=readme-ov-file#scales), etc.) for the component. If you don't need all of the global styles, peek at the docs for [including assets](https://github.com/adobe/spectrum-css?tab=readme-ov-file#including-assets)). Use this file by including the stylesheet (plus stylesheets for dependencies) in the `<head>` tag:

```html
<head>
<!-- Include global tokens depedency first -->
<link
rel="stylesheet"
href="node_modules/@spectrum-css/tokens/dist/index.css"
/>

<!-- Include index.css for the components you're using -->
<link
rel="stylesheet"
href="node_modules/@spectrum-css/button/dist/index.css"
/>
<!-- Include global tokens depedency first -->
<link
rel="stylesheet"
href="node_modules/@spectrum-css/tokens/dist/index.css"
/>

<!-- Include index.css for the components you're using -->
<link
rel="stylesheet"
href="node_modules/@spectrum-css/button/dist/index.css"
/>
</head>
```

Inside the `<body>` tag, add the markup for your component (Spectrum button in our example). The example also includes the CSS class names `spectrum-Button--fill` and `spectrum-Button--accent`, to use the accent variant:

```html
<button
class="spectrum-Button spectrum-Button--fill spectrum-Button--accent spectrum-Button--sizeM"
class="spectrum-Button spectrum-Button--fill spectrum-Button--accent spectrum-Button--sizeM"
>
<span class="spectrum-Button-label">Button</span>
<span class="spectrum-Button-label">Button</span>
</button>
```

To put it all together, your final html file will look like this:

```html
<html class="spectrum spectrum--light spectrum--medium">
<head>
<link
rel="stylesheet"
href="node_modules/@spectrum-css/tokens/dist/index.css"
/>
<link
rel="stylesheet"
href="node_modules/@spectrum-css/button/dist/index.css"
/>
</head>
<body>
<button
class="spectrum-Button spectrum-Button--fill spectrum-Button--accent spectrum-Button--sizeM"
>
<span class="spectrum-Button-label">Button</span>
</button>
</body>
<head>
<link
rel="stylesheet"
href="node_modules/@spectrum-css/tokens/dist/index.css"
/>
<link
rel="stylesheet"
href="node_modules/@spectrum-css/button/dist/index.css"
/>
</head>
<body>
<button
class="spectrum-Button spectrum-Button--fill spectrum-Button--accent spectrum-Button--sizeM"
>
<span class="spectrum-Button-label">Button</span>
</button>
</body>
</html>
```

Expand Down
18 changes: 10 additions & 8 deletions .github/actions/file-diff/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@
* governing permissions and limitations under the License.
*/

const { existsSync } = require("fs");
const { join, sep } = require("path");
import { existsSync } from "fs";
import { join, sep } from "path";

const core = require("@actions/core");
import core from "@actions/core";

const {
fetchFilesAndSizes,
bytesToSize,
import {
addComment,
} = require("./utilities.js");
bytesToSize,
fetchFilesAndSizes,
} from "./utilities.js";

async function run() {
try {
Expand Down Expand Up @@ -415,7 +415,9 @@ const makeTable = function (PACKAGES, filePath, rootPath) {
let mainFile = "index.css";
if (existsSync(packagePath)) {
// If the package.json exists, read in the main file
const { main } = require(packagePath) ?? {};
const packageContent = fs.readFileSync(packagePath, "utf8") ?? "{}";
const { main } = JSON.parse(packageContent);

// If the main file is a string, use it as the main file
if (typeof main === "string") {
// Strip out the path to the dist folder from the main file
Expand Down
32 changes: 16 additions & 16 deletions .github/actions/file-diff/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
* governing permissions and limitations under the License.
*/

const { statSync, existsSync, readdirSync } = require("fs");
const { join, relative } = require("path");
import { existsSync, readdirSync, statSync } from "fs";
import { join, relative } from "path";

const github = require("@actions/github");
const glob = require("@actions/glob");
import { context, getOctokit } from "@actions/github";
import { create } from "@actions/glob";

/**
* List all files in the directory to help with debugging
Expand All @@ -39,7 +39,7 @@ function debugEmptyDirectory(path, pattern, { core }) {
if (dirent.isFile()) {
const file = join(path, dirent.name);
if (dirent.name.startsWith(".")) return;
core.info(`- ${relative(path, file)} | ${exports.bytesToSize(statSync(file).size)}`);
core.info(`- ${relative(path, file)} | ${bytesToSize(statSync(file).size)}`);
} else if (dirent.isDirectory()) {
const dir = join(path, dirent.name);
if (dirent.name.startsWith(".") || dirent.name === "node_modules") return;
Expand All @@ -61,7 +61,7 @@ function debugEmptyDirectory(path, pattern, { core }) {
* @param {number} bytes
* @returns {string} The size in human readable format
*/
exports.bytesToSize = function (bytes) {
export function bytesToSize (bytes) {
if (!bytes) return "-";
if (bytes === 0) return "0";

Expand All @@ -77,7 +77,7 @@ exports.bytesToSize = function (bytes) {
}

return (bytes / Math.pow(1024, i)).toFixed(2) + " " + sizes[i];
};
}

/** @typedef {import('@octokit/rest').RestEndpointMethodTypes['issues']} Issues */
/**
Expand All @@ -86,20 +86,20 @@ exports.bytesToSize = function (bytes) {
* @param {string} token - The GitHub token to use for authentication
* @returns {Promise<Issues['createComment']['response'] | Issues['updateComment']['response']>}
*/
exports.addComment = async function ({ search, content, token }) {
export async function addComment ({ search, content, token }) {
/**
* @description Set up the octokit client
* @type ReturnType<import('@actions/github').getOctokit>
*/
const octokit = new github.getOctokit(token);
const octokit = new getOctokit(token);

// Fetch data about the action that triggered the workflow
/** @type import('@actions/github/lib/interfaces').WebhookPayload['pull_request'] */
const pullRequest = github.context.payload.pull_request;
const pullRequest = context.payload.pull_request;
/** @type string */
const owner = github.context.payload.repository.owner.login;
const owner = context.payload.repository.owner.login;
/** @type string */
const repo = github.context.payload.repository.name;
const repo = context.payload.repository.name;

if (!pullRequest) {
core.warning(`No pull request found in the context, skipping comment`);
Expand Down Expand Up @@ -141,7 +141,7 @@ exports.addComment = async function ({ search, content, token }) {
}

return octokit.rest.issues[action](commentData);
};
}

/**
* Use the provided glob pattern to fetch the files and their sizes from the
Expand All @@ -150,11 +150,11 @@ exports.addComment = async function ({ search, content, token }) {
* @param {string[]} patterns
* @returns {Promise<Map<string, number>>} - Returns the relative path and size of the files
*/
exports.fetchFilesAndSizes = async function (rootPath, patterns = [], { core }) {
export async function fetchFilesAndSizes (rootPath, patterns = [], { core }) {
if (!existsSync(rootPath)) return new Map();

/** @type import('@actions/glob').Globber */
const globber = await glob.create(patterns.map((f) => join(rootPath, f)).join("\n"));
const globber = await create(patterns.map((f) => join(rootPath, f)).join("\n"));

/** @type Awaited<ReturnType<import('@actions/glob').Globber['glob']>> */
const files = await globber.glob();
Expand All @@ -178,4 +178,4 @@ exports.fetchFilesAndSizes = async function (rootPath, patterns = [], { core })
})
.filter(Boolean),
);
};
}
2 changes: 2 additions & 0 deletions .github/workflows/development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ jobs:
styles_modified_files: ${{ needs.changed_files.outputs.styles_modified_files }}
eslint_added_files: ${{ needs.changed_files.outputs.eslint_added_files }}
eslint_modified_files: ${{ needs.changed_files.outputs.eslint_modified_files }}
mdlint_added_files: ${{ needs.changed_files.outputs.mdlint_added_files }}
mdlint_modified_files: ${{ needs.changed_files.outputs.mdlint_modified_files }}
secrets: inherit

# -------------------------------------------------------------
Expand Down
12 changes: 2 additions & 10 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
{
"tabWidth": 2,
"useTabs": true,
"overrides": [
{
"files": "*.css",
"options": {
"printWidth": 500
}
}
]
"tabWidth": 2,
"useTabs": false
}
Loading
Loading