From d27a9528cbbf53ca15906c73709abcaea3f0c6d4 Mon Sep 17 00:00:00 2001 From: John Kreitlow <863023+radium-v@users.noreply.github.com> Date: Wed, 27 May 2026 23:29:52 -0700 Subject: [PATCH 1/7] feat(web-components): generate SSR templates and stylesheets into src/ and copy into dist during compile --- packages/web-components/package.json | 7 +- packages/web-components/scripts/compile.js | 54 +++-- .../web-components/scripts/generate-ssr.js | 219 ++++++++++++++++++ 3 files changed, 259 insertions(+), 21 deletions(-) create mode 100644 packages/web-components/scripts/generate-ssr.js diff --git a/packages/web-components/package.json b/packages/web-components/package.json index cdaab2491138d..14f6d52ca4693 100644 --- a/packages/web-components/package.json +++ b/packages/web-components/package.json @@ -79,11 +79,10 @@ "compile:benchmark": "rollup -c rollup.bench.js", "clean": "node ./scripts/clean dist", "generate-api": "api-extractor run --local", - "build": "yarn compile && yarn build:rollup && yarn build:ssr && yarn generate-api && yarn analyze", - "build:ssr:templates": "fast-test-harness generate-templates --tag-prefix=fluent", - "build:ssr:styles": "fast-test-harness generate-stylesheets", - "build:ssr": "yarn build:ssr:templates && yarn build:ssr:styles", + "build": "yarn compile && yarn build:rollup && yarn generate-api && yarn analyze", "build:rollup": "rollup -c", + "generate:ssr": "node ./scripts/generate-ssr.js", + "check:ssr": "node ./scripts/generate-ssr.js --check", "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", "format": "prettier -w src/**/*.{ts,html} src/*.{ts,html} --ignore-path ../../.prettierignore", diff --git a/packages/web-components/scripts/compile.js b/packages/web-components/scripts/compile.js index b57091b02a0f7..162ff7e79a898 100644 --- a/packages/web-components/scripts/compile.js +++ b/packages/web-components/scripts/compile.js @@ -1,27 +1,47 @@ /* eslint-disable no-undef */ -import { execSync } from 'child_process'; -import chalk from 'chalk'; - -main(); +import { execSync } from 'node:child_process'; +import { cp, glob, mkdir } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; -function compile() { - try { - console.log(chalk.bold(`🎬 compile:start`)); +import chalk from 'chalk'; - console.log(chalk.blueBright(`compile: generating design tokens`)); - execSync(`node ./scripts/generate-tokens`, { stdio: 'inherit' }); +const SRC = 'src'; +const OUT = 'dist/esm'; - console.log(chalk.blueBright(`compile: running tsc`)); - execSync(`tsc -p tsconfig.lib.json --rootDir ./src --baseUrl .`, { stdio: 'inherit' }); +async function copySsrAssets() { + const patterns = ['**/*.template.html', '**/*.styles.css']; + let count = 0; - console.log(chalk.bold(`🏁 compile:end`)); - } catch (err) { - console.error(err); - process.exit(1); + for (const pattern of patterns) { + for await (const file of glob(pattern, { cwd: SRC })) { + const from = join(SRC, file); + const to = join(OUT, file); + await mkdir(dirname(to), { recursive: true }); + await cp(from, to); + count++; + } } + + console.log(chalk.dim(`compile: copied ${count} SSR asset${count === 1 ? '' : 's'} from ${SRC}/ → ${OUT}/`)); } -function main() { - compile(); +async function compile() { + console.log(chalk.bold(`🎬 compile:start`)); + + console.log(chalk.blueBright(`compile: generating design tokens`)); + execSync(`node ./scripts/generate-tokens`, { stdio: 'inherit' }); + + console.log(chalk.blueBright(`compile: running tsc`)); + execSync(`tsc -p tsconfig.lib.json --rootDir ./src --baseUrl .`, { stdio: 'inherit' }); + + console.log(chalk.blueBright(`compile: copying SSR assets`)); + await copySsrAssets(); + + console.log(chalk.bold(`🏁 compile:end`)); } + +compile().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/packages/web-components/scripts/generate-ssr.js b/packages/web-components/scripts/generate-ssr.js new file mode 100644 index 0000000000000..eb388fa7b21f5 --- /dev/null +++ b/packages/web-components/scripts/generate-ssr.js @@ -0,0 +1,219 @@ +/* eslint-disable no-undef */ + +/** + * Regenerates SSR template HTML and stylesheet CSS files next to their + * source `.template.ts` / `.styles.ts` counterparts in `src/`. + * + * Flow: + * 1. Compile `src/` to a throwaway temp dir so we have JS modules with + * runtime metadata for the generators to walk. + * 2. Run `generate-templates` and `generate-stylesheets` from the + * `@microsoft/fast-test-harness` library, writing back into `src/` + * while preserving the per-component subdirectory structure. + * + * The generated files should be committed to the repo; the normal `compile` script + * copies them into `dist/esm/`. + * + * Running this script is only necessary when making changes to the source `.template.ts` or `.styles.ts` files, + * and should be part of the development workflow when working on those files. The generated files can then + * be modified as needed for SSR purposes, and those modifications should also be committed to the repo. + * + * Pass `--check` to compare what regeneration would produce against the + * current working tree without writing. Each output file is classified + * against the matrix of (TS@HEAD vs TS@working) × (HTML@HEAD vs + * HTML@working) × (regen output vs disk). Exits non-zero when any + * stale, hand-edited, or conflicting files are detected. + */ + +import { execSync } from 'node:child_process'; +import { mkdirSync, mkdtempSync, rmSync } from 'node:fs'; +import { glob, readFile } from 'node:fs/promises'; +import { join, relative } from 'node:path'; + +import chalk from 'chalk'; +import prettier from 'prettier'; + +import { generateStylesheets } from '@microsoft/fast-test-harness/build/generate-stylesheets.js'; +import { generateFTemplates } from '@microsoft/fast-test-harness/build/generate-templates.js'; + +const cwd = process.cwd(); +const TEMP_PARENT = join(cwd, 'temp'); +const checkMode = process.argv.includes('--check'); +const label = checkMode ? 'generate:ssr:check' : 'generate:ssr'; + +async function main() { + mkdirSync(TEMP_PARENT, { recursive: true }); + const tempDir = mkdtempSync(join(TEMP_PARENT, 'ssr-')); + const stagingDir = checkMode ? mkdtempSync(join(TEMP_PARENT, 'ssr-staging-')) : null; + const prettierConfig = (await prettier.resolveConfig(cwd)) ?? {}; + const outDir = stagingDir ? relative(cwd, stagingDir) : 'src'; + let exitCode = 0; + + try { + console.log(chalk.bold(`🎬 ${label} start`)); + + console.log(chalk.blueBright(`${label}: compiling src → ${tempDir}`)); + execSync(`tsc -p tsconfig.lib.json --rootDir ./src --baseUrl . --outDir ${tempDir} --declaration false`, { + stdio: 'inherit', + }); + + console.log(chalk.blueBright(`${label}: writing *.template.html → ${outDir}/`)); + await generateFTemplates({ + cwd, + distDir: tempDir, + outDir, + tagPrefix: 'fluent', + format: content => + prettier.format(content, { ...prettierConfig, parser: 'html', htmlWhitespaceSensitivity: 'ignore' }), + }); + + console.log(chalk.blueBright(`${label}: writing *.styles.css → ${outDir}/`)); + await generateStylesheets({ + cwd, + distDir: tempDir, + outDir, + format: content => prettier.format(content, { ...prettierConfig, parser: 'css' }), + }); + + if (checkMode) { + const result = await classify(stagingDir); + printSummary(result); + if (result.stale.length || result.handEdited.length || result.conflicts.length) { + exitCode = 1; + } + } + + console.log(chalk.bold(`🏁 ${label} end`)); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + if (stagingDir) rmSync(stagingDir, { recursive: true, force: true }); + } + + if (exitCode) process.exit(exitCode); +} + +/** + * Classify each staged file against the working tree using the + * four-state matrix. Returns counts plus per-bucket file lists. + * + * - `unchanged` — regen produces the file already on disk + * - `created` — no committed baseline for this HTML/CSS + * - `updated` — TS changed; regen produces new HTML/CSS (normal flow) + * - `stale` — TS and HTML both at HEAD, but regen disagrees with disk + * (committed state is out of sync — CI failure signal) + * - `handEdited` — HTML differs from HEAD with no TS change; regen would clobber + * - `conflicts` — both TS and HTML differ from HEAD, and regen disagrees with disk + */ +async function classify(stagingDir) { + const dirtyMap = buildDirtyMap(); + // git status --porcelain paths are relative to the repo root; lookups + // need to be prefixed with the path from repo root to cwd. + const repoPrefix = execSync('git rev-parse --show-prefix', { cwd, encoding: 'utf8' }).trim(); + const dirtyStatus = path => statusOf(dirtyMap.get(repoPrefix + path)); + const result = { + unchanged: [], + created: [], + updated: [], + stale: [], + handEdited: [], + conflicts: [], + }; + + for (const pattern of ['**/*.template.html', '**/*.styles.css']) { + for await (const file of glob(pattern, { cwd: stagingDir })) { + const stagedAbs = join(stagingDir, file); + const srcHtmlAbs = join(cwd, 'src', file); + const srcHtmlRel = join('src', file); + + let srcTsRel; + if (file.endsWith('.template.html')) { + srcTsRel = join('src', file.replace(/\.template\.html$/, '.template.ts')); + } else if (file.endsWith('.styles.css')) { + srcTsRel = join('src', file.replace(/\.styles\.css$/, '.styles.ts')); + } else { + continue; + } + + const tsStatus = dirtyStatus(srcTsRel); + const htmlStatus = dirtyStatus(srcHtmlRel); + const newHtml = await readFile(stagedAbs, 'utf8'); + const onDiskHtml = await readFile(srcHtmlAbs, 'utf8').catch(() => null); + + if (newHtml === onDiskHtml) { + result.unchanged.push(file); + continue; + } + + if (htmlStatus === 'new') { + result.created.push(file); + continue; + } + + if (htmlStatus === 'same') { + if (tsStatus === 'same') { + result.stale.push(file); + } else { + result.updated.push(file); + } + continue; + } + + // htmlStatus === 'changed' + if (tsStatus === 'same') { + result.handEdited.push(file); + } else { + result.conflicts.push(file); + } + } + } + + return result; +} + +/** + * One git invocation that returns a map of {pathRelativeToCwd → status code} + * for every file that differs from HEAD (modified, added, untracked, etc.). + * Tracked files in sync with HEAD are absent from the map. + */ +function buildDirtyMap() { + const map = new Map(); + const output = execSync('git status --porcelain=v1 -uall', { cwd, encoding: 'utf8' }); + for (const line of output.split('\n')) { + if (!line) continue; + const code = line.slice(0, 2); + let path = line.slice(3); + if (path.startsWith('"')) path = JSON.parse(path); + const arrow = path.indexOf(' -> '); // rename: "old -> new" + if (arrow !== -1) path = path.slice(arrow + 4); + map.set(path, code); + } + return map; +} + +function statusOf(code) { + if (!code) return 'same'; + if (code[0] === '?' || code[0] === 'A') return 'new'; + return 'changed'; +} + +function printSummary({ unchanged, created, updated, stale, handEdited, conflicts }) { + console.log(chalk.bold(`\n${label} summary:`)); + console.log(chalk.green(` ✓ ${unchanged.length} unchanged`)); + printBucket(chalk.cyan, '+', created, 'new (no committed baseline)'); + printBucket(chalk.blue, '✎', updated, 'would update (TS changed)'); + printBucket(chalk.yellow, '!', stale, 'stale (committed HTML out of sync with committed TS)'); + printBucket(chalk.yellow, '⚠', handEdited, 'hand-edited (HTML differs from HEAD, TS unchanged)'); + printBucket(chalk.red, '✘', conflicts, 'conflicts (TS and HTML both differ from HEAD, regen disagrees with disk)'); + console.log(''); +} + +function printBucket(color, glyph, files, description) { + if (files.length === 0) return; + console.log(color(` ${glyph} ${files.length} ${description}`)); + for (const f of files) console.log(chalk.dim(` ${f}`)); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); From 7ea587dc655bfeeb09b0c7dadb8b128ea883d7e4 Mon Sep 17 00:00:00 2001 From: John Kreitlow <863023+radium-v@users.noreply.github.com> Date: Wed, 27 May 2026 23:30:12 -0700 Subject: [PATCH 2/7] fix(web-components): close CSS blocks in dialog-body and dropdown styles --- packages/web-components/src/dialog-body/dialog-body.styles.ts | 1 + packages/web-components/src/dropdown/dropdown.styles.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/web-components/src/dialog-body/dialog-body.styles.ts b/packages/web-components/src/dialog-body/dialog-body.styles.ts index 9109a57ce4895..fc3586996228b 100644 --- a/packages/web-components/src/dialog-body/dialog-body.styles.ts +++ b/packages/web-components/src/dialog-body/dialog-body.styles.ts @@ -106,4 +106,5 @@ export const styles = css` position: sticky; z-index: 2; } + } `; diff --git a/packages/web-components/src/dropdown/dropdown.styles.ts b/packages/web-components/src/dropdown/dropdown.styles.ts index 5e840d06ab6b2..0e52c6e03a7fe 100644 --- a/packages/web-components/src/dropdown/dropdown.styles.ts +++ b/packages/web-components/src/dropdown/dropdown.styles.ts @@ -260,4 +260,5 @@ export const styles = css` :host(:disabled) :where(slot[name='indicator'] > *, ::slotted([slot='indicator'])) { color: GrayText; } + } `; From 46726de5d760b464d07daa6f672528366cc05e0b Mon Sep 17 00:00:00 2001 From: John Kreitlow <863023+radium-v@users.noreply.github.com> Date: Wed, 27 May 2026 23:31:50 -0700 Subject: [PATCH 3/7] chore(web-components): commit generated SSR templates and stylesheets --- .../accordion-item/accordion-item.styles.css | 174 +++++++ .../accordion-item.template.html | 54 ++ .../src/accordion/accordion.styles.css | 12 + .../src/accordion/accordion.template.html | 6 + .../anchor-button/anchor-button.styles.css | 226 +++++++++ .../anchor-button/anchor-button.template.html | 10 + .../src/avatar/avatar.styles.css | 480 ++++++++++++++++++ .../src/avatar/avatar.template.html | 13 + .../web-components/src/badge/badge.styles.css | 266 ++++++++++ .../src/badge/badge.template.html | 8 + .../src/button/button.styles.css | 268 ++++++++++ .../src/button/button.template.html | 10 + .../src/checkbox/checkbox.styles.css | 172 +++++++ .../src/checkbox/checkbox.template.html | 29 ++ .../compound-button.styles.css | 370 ++++++++++++++ .../compound-button.template.html | 11 + .../counter-badge/counter-badge.styles.css | 177 +++++++ .../counter-badge/counter-badge.template.html | 8 + .../src/dialog-body/dialog-body.styles.css | 92 ++++ .../src/dialog-body/dialog-body.template.html | 12 + .../src/dialog/dialog.styles.css | 85 ++++ .../src/dialog/dialog.template.html | 19 + .../src/divider/divider.styles.css | 123 +++++ .../src/divider/divider.template.html | 6 + .../src/drawer-body/drawer-body.styles.css | 36 ++ .../src/drawer-body/drawer-body.template.html | 15 + .../src/drawer/drawer.styles.css | 134 +++++ .../src/drawer/drawer.template.html | 21 + .../src/dropdown/dropdown.styles.css | 223 ++++++++ .../src/dropdown/dropdown.template.html | 28 + .../web-components/src/field/field.styles.css | 125 +++++ .../src/field/field.template.html | 13 + .../web-components/src/image/image.styles.css | 50 ++ .../src/image/image.template.html | 6 + .../web-components/src/label/label.styles.css | 40 ++ .../src/label/label.template.html | 7 + .../web-components/src/link/link.styles.css | 72 +++ .../src/link/link.template.html | 6 + .../src/listbox/listbox.styles.css | 55 ++ .../src/listbox/listbox.template.html | 6 + .../src/menu-button/menu-button.styles.css | 268 ++++++++++ .../src/menu-button/menu-button.template.html | 25 + .../src/menu-item/menu-item.styles.css | 156 ++++++ .../src/menu-item/menu-item.template.html | 50 ++ .../src/menu-list/menu-list.styles.css | 20 + .../src/menu-list/menu-list.template.html | 6 + .../web-components/src/menu/menu.styles.css | 57 +++ .../src/menu/menu.template.html | 8 + .../src/message-bar/message-bar.styles.css | 97 ++++ .../src/message-bar/message-bar.template.html | 13 + .../src/option/option.styles.css | 134 +++++ .../src/option/option.template.html | 24 + .../src/progress-bar/progress-bar.styles.css | 103 ++++ .../progress-bar/progress-bar.template.html | 6 + .../src/radio-group/radio-group.styles.css | 45 ++ .../src/radio-group/radio-group.template.html | 13 + .../web-components/src/radio/radio.styles.css | 119 +++++ .../src/radio/radio.template.html | 8 + .../rating-display/rating-display.styles.css | 142 ++++++ .../rating-display.template.html | 9 + .../src/slider/slider.styles.css | 193 +++++++ .../src/slider/slider.template.html | 15 + .../src/spinner/spinner.styles.css | 159 ++++++ .../src/spinner/spinner.template.html | 18 + .../src/switch/switch.styles.css | 125 +++++ .../src/switch/switch.template.html | 11 + .../web-components/src/tab/tab.styles.css | 122 +++++ .../web-components/src/tab/tab.template.html | 8 + .../src/tablist/tablist.styles.css | 207 ++++++++ .../src/tablist/tablist.template.html | 6 + .../src/text-input/text-input.styles.css | 204 ++++++++ .../src/text-input/text-input.template.html | 36 ++ .../web-components/src/text/text.styles.css | 108 ++++ .../src/text/text.template.html | 6 + .../src/textarea/textarea.styles.css | 262 ++++++++++ .../src/textarea/textarea.template.html | 30 ++ .../toggle-button/toggle-button.styles.css | 361 +++++++++++++ .../toggle-button/toggle-button.template.html | 10 + .../src/tooltip/tooltip.styles.css | 90 ++++ .../src/tooltip/tooltip.template.html | 6 + .../src/tree-item/tree-item.styles.css | 161 ++++++ .../src/tree-item/tree-item.template.html | 27 + .../web-components/src/tree/tree.styles.css | 10 + .../src/tree/tree.template.html | 13 + 84 files changed, 6959 insertions(+) create mode 100644 packages/web-components/src/accordion-item/accordion-item.styles.css create mode 100644 packages/web-components/src/accordion-item/accordion-item.template.html create mode 100644 packages/web-components/src/accordion/accordion.styles.css create mode 100644 packages/web-components/src/accordion/accordion.template.html create mode 100644 packages/web-components/src/anchor-button/anchor-button.styles.css create mode 100644 packages/web-components/src/anchor-button/anchor-button.template.html create mode 100644 packages/web-components/src/avatar/avatar.styles.css create mode 100644 packages/web-components/src/avatar/avatar.template.html create mode 100644 packages/web-components/src/badge/badge.styles.css create mode 100644 packages/web-components/src/badge/badge.template.html create mode 100644 packages/web-components/src/button/button.styles.css create mode 100644 packages/web-components/src/button/button.template.html create mode 100644 packages/web-components/src/checkbox/checkbox.styles.css create mode 100644 packages/web-components/src/checkbox/checkbox.template.html create mode 100644 packages/web-components/src/compound-button/compound-button.styles.css create mode 100644 packages/web-components/src/compound-button/compound-button.template.html create mode 100644 packages/web-components/src/counter-badge/counter-badge.styles.css create mode 100644 packages/web-components/src/counter-badge/counter-badge.template.html create mode 100644 packages/web-components/src/dialog-body/dialog-body.styles.css create mode 100644 packages/web-components/src/dialog-body/dialog-body.template.html create mode 100644 packages/web-components/src/dialog/dialog.styles.css create mode 100644 packages/web-components/src/dialog/dialog.template.html create mode 100644 packages/web-components/src/divider/divider.styles.css create mode 100644 packages/web-components/src/divider/divider.template.html create mode 100644 packages/web-components/src/drawer-body/drawer-body.styles.css create mode 100644 packages/web-components/src/drawer-body/drawer-body.template.html create mode 100644 packages/web-components/src/drawer/drawer.styles.css create mode 100644 packages/web-components/src/drawer/drawer.template.html create mode 100644 packages/web-components/src/dropdown/dropdown.styles.css create mode 100644 packages/web-components/src/dropdown/dropdown.template.html create mode 100644 packages/web-components/src/field/field.styles.css create mode 100644 packages/web-components/src/field/field.template.html create mode 100644 packages/web-components/src/image/image.styles.css create mode 100644 packages/web-components/src/image/image.template.html create mode 100644 packages/web-components/src/label/label.styles.css create mode 100644 packages/web-components/src/label/label.template.html create mode 100644 packages/web-components/src/link/link.styles.css create mode 100644 packages/web-components/src/link/link.template.html create mode 100644 packages/web-components/src/listbox/listbox.styles.css create mode 100644 packages/web-components/src/listbox/listbox.template.html create mode 100644 packages/web-components/src/menu-button/menu-button.styles.css create mode 100644 packages/web-components/src/menu-button/menu-button.template.html create mode 100644 packages/web-components/src/menu-item/menu-item.styles.css create mode 100644 packages/web-components/src/menu-item/menu-item.template.html create mode 100644 packages/web-components/src/menu-list/menu-list.styles.css create mode 100644 packages/web-components/src/menu-list/menu-list.template.html create mode 100644 packages/web-components/src/menu/menu.styles.css create mode 100644 packages/web-components/src/menu/menu.template.html create mode 100644 packages/web-components/src/message-bar/message-bar.styles.css create mode 100644 packages/web-components/src/message-bar/message-bar.template.html create mode 100644 packages/web-components/src/option/option.styles.css create mode 100644 packages/web-components/src/option/option.template.html create mode 100644 packages/web-components/src/progress-bar/progress-bar.styles.css create mode 100644 packages/web-components/src/progress-bar/progress-bar.template.html create mode 100644 packages/web-components/src/radio-group/radio-group.styles.css create mode 100644 packages/web-components/src/radio-group/radio-group.template.html create mode 100644 packages/web-components/src/radio/radio.styles.css create mode 100644 packages/web-components/src/radio/radio.template.html create mode 100644 packages/web-components/src/rating-display/rating-display.styles.css create mode 100644 packages/web-components/src/rating-display/rating-display.template.html create mode 100644 packages/web-components/src/slider/slider.styles.css create mode 100644 packages/web-components/src/slider/slider.template.html create mode 100644 packages/web-components/src/spinner/spinner.styles.css create mode 100644 packages/web-components/src/spinner/spinner.template.html create mode 100644 packages/web-components/src/switch/switch.styles.css create mode 100644 packages/web-components/src/switch/switch.template.html create mode 100644 packages/web-components/src/tab/tab.styles.css create mode 100644 packages/web-components/src/tab/tab.template.html create mode 100644 packages/web-components/src/tablist/tablist.styles.css create mode 100644 packages/web-components/src/tablist/tablist.template.html create mode 100644 packages/web-components/src/text-input/text-input.styles.css create mode 100644 packages/web-components/src/text-input/text-input.template.html create mode 100644 packages/web-components/src/text/text.styles.css create mode 100644 packages/web-components/src/text/text.template.html create mode 100644 packages/web-components/src/textarea/textarea.styles.css create mode 100644 packages/web-components/src/textarea/textarea.template.html create mode 100644 packages/web-components/src/toggle-button/toggle-button.styles.css create mode 100644 packages/web-components/src/toggle-button/toggle-button.template.html create mode 100644 packages/web-components/src/tooltip/tooltip.styles.css create mode 100644 packages/web-components/src/tooltip/tooltip.template.html create mode 100644 packages/web-components/src/tree-item/tree-item.styles.css create mode 100644 packages/web-components/src/tree-item/tree-item.template.html create mode 100644 packages/web-components/src/tree/tree.styles.css create mode 100644 packages/web-components/src/tree/tree.template.html diff --git a/packages/web-components/src/accordion-item/accordion-item.styles.css b/packages/web-components/src/accordion-item/accordion-item.styles.css new file mode 100644 index 0000000000000..f153a1bc1ce27 --- /dev/null +++ b/packages/web-components/src/accordion-item/accordion-item.styles.css @@ -0,0 +1,174 @@ +:host([hidden]) { + display: none; +} +:host { + display: block; +} + +:host { + max-width: fit-content; + contain: content; +} + +.heading { + height: 44px; + display: grid; + position: relative; + padding-inline: var(--spacingHorizontalM) var(--spacingHorizontalMNudge); + border-radius: var(--borderRadiusMedium); + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + font-weight: var(--fontWeightRegular); + line-height: var(--lineHeightBase300); + grid-template-columns: auto auto 1fr auto; +} + +.button { + appearance: none; + background: var(--colorTransparentBackground); + border: none; + box-sizing: border-box; + color: var(--colorNeutralForeground1); + cursor: pointer; + font: inherit; + grid-column: auto / span 2; + grid-row: 1; + height: 44px; + outline: none; + padding: 0; + text-align: start; +} + +.button::before { + content: ''; + position: absolute; + inset: 0px; + cursor: pointer; + border-radius: var(--borderRadiusSmall); +} + +:where(.default-marker-collapsed, .default-marker-expanded), +::slotted(:is([slot='marker-collapsed'], [slot='marker-expanded'])) { + display: flex; + align-items: center; + justify-content: center; + pointer-events: none; + position: relative; + height: 100%; + padding-inline-end: var(--spacingHorizontalS); + grid-column: 1 / span 1; + grid-row: 1; +} + +.content { + margin: 0 var(--spacingHorizontalM); +} + +::slotted([slot='start']) { + display: flex; + justify-content: center; + align-items: center; + padding-right: var(--spacingHorizontalS); + grid-column: 2 / span 1; + grid-row: 1; +} + +button:focus-visible::after { + content: ''; + position: absolute; + inset: 0px; + cursor: pointer; + border-radius: var(--borderRadiusSmall); + outline: none; + border: 2px solid var(--colorStrokeFocus1); + box-shadow: inset 0 0 0 1px var(--colorStrokeFocus2); +} + +/* --- Disabled attr styles --- */ + +:host([disabled]) .button { + color: var(--colorNeutralForegroundDisabled); +} + +:host([disabled]) svg { + filter: invert(89%) sepia(0%) saturate(569%) hue-rotate(155deg) brightness(88%) contrast(87%); +} + +/* --- Expanded attr styles --- */ + +:host([expanded]) .content { + display: block; +} + +:host([expanded]) .default-marker-collapsed, +:host([expanded]) ::slotted([slot='marker-collapsed']), +:host(:not([expanded])) :is(.default-marker-expanded, .content), +:host(:not([expanded])) ::slotted([slot='marker-expanded']) { + display: none; +} + +:host([expanded]) ::slotted([slot='marker-expanded']), +:host(:not([expanded])) ::slotted([slot='marker-collapsed']) { + display: flex; +} + +/* --- Appearance attr styles --- */ + +.heading { + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); +} + +:host([size='small']) .heading { + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); +} + +:host([size='large']) .heading { + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} + +:host([size='extra-large']) .heading { + font-size: var(--fontSizeBase500); + line-height: var(--lineHeightBase500); +} + +/* --- marker-position attr styles --- */ + +:host([marker-position='end']) ::slotted([slot='start']) { + grid-column: 1 / span 1; +} + +:host([marker-position='end']) :is(.default-marker-collapsed, .default-marker-expanded) { + grid-column: 4 / span 1; + padding-inline-start: var(--spacingHorizontalS); + padding-inline-end: 0; +} + +:host([marker-position='end']) .button { + grid-column: 2 / span 3; +} + +/* --- Block attr styles --- */ + +:host([block]) { + max-width: 100%; +} + +:host([marker-position='end']) .heading { + grid-template-columns: auto auto 28px; + padding-inline: var(--spacingHorizontalM); +} + +:host([marker-position='end']:has([slot='start'])) .heading { + padding-inline: var(--spacingHorizontalMNudge) var(--spacingHorizontalM); +} + +:host([block][marker-position='end']) .heading { + grid-template-columns: auto 1fr; +} + +:host([marker-position='end']) :is(.default-marker-collapsed, .default-marker-expanded) { + grid-column: 5 / span 1; +} diff --git a/packages/web-components/src/accordion-item/accordion-item.template.html b/packages/web-components/src/accordion-item/accordion-item.template.html new file mode 100644 index 0000000000000..092f2a3ebb224 --- /dev/null +++ b/packages/web-components/src/accordion-item/accordion-item.template.html @@ -0,0 +1,54 @@ + + + diff --git a/packages/web-components/src/accordion/accordion.styles.css b/packages/web-components/src/accordion/accordion.styles.css new file mode 100644 index 0000000000000..ebc048c03ece2 --- /dev/null +++ b/packages/web-components/src/accordion/accordion.styles.css @@ -0,0 +1,12 @@ +:host([hidden]) { + display: none; +} +:host { + display: flex; +} + +:host { + flex-direction: column; + width: 100%; + contain: content; +} diff --git a/packages/web-components/src/accordion/accordion.template.html b/packages/web-components/src/accordion/accordion.template.html new file mode 100644 index 0000000000000..52563ec8181b5 --- /dev/null +++ b/packages/web-components/src/accordion/accordion.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/anchor-button/anchor-button.styles.css b/packages/web-components/src/anchor-button/anchor-button.styles.css new file mode 100644 index 0000000000000..072fbff48f853 --- /dev/null +++ b/packages/web-components/src/anchor-button/anchor-button.styles.css @@ -0,0 +1,226 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + --icon-spacing: var(--spacingHorizontalSNudge); + position: relative; + contain: layout style; + vertical-align: middle; + align-items: center; + box-sizing: border-box; + justify-content: center; + text-align: center; + text-decoration-line: none; + margin: 0; + min-height: 32px; + outline-style: none; + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground1); + border: var(--strokeWidthThin) solid var(--colorNeutralStroke1); + padding: 0 var(--spacingHorizontalM); + min-width: 96px; + border-radius: var(--borderRadiusMedium); + font-size: var(--fontSizeBase300); + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + line-height: var(--lineHeightBase300); + transition-duration: var(--durationFaster); + transition-property: background, border, color; + transition-timing-function: var(--curveEasyEase); + cursor: pointer; + user-select: none; +} + +.content { + display: inherit; +} + +:host(:hover) { + background-color: var(--colorNeutralBackground1Hover); + color: var(--colorNeutralForeground1Hover); + border-color: var(--colorNeutralStroke1Hover); +} + +:host(:hover:active) { + background-color: var(--colorNeutralBackground1Pressed); + border-color: var(--colorNeutralStroke1Pressed); + color: var(--colorNeutralForeground1Pressed); + outline-style: none; +} + +:host(:focus-visible) { + border-color: var(--colorTransparentStroke); + outline: var(--strokeWidthThick) solid var(--colorTransparentStroke); + box-shadow: var(--shadow4), 0 0 0 2px var(--colorStrokeFocus2); +} + +@media screen and (prefers-reduced-motion: reduce) { + :host { + transition-duration: 0.01ms; + } +} + +::slotted(svg) { + font-size: 20px; + height: 20px; + width: 20px; + fill: currentColor; +} + +::slotted([slot='start']) { + margin-inline-end: var(--icon-spacing); +} + +::slotted([slot='end']), +[slot='end'] { + flex-shrink: 0; + margin-inline-start: var(--icon-spacing); +} + +:host([icon-only]) { + min-width: 32px; + max-width: 32px; +} + +:host([size='small']) { + --icon-spacing: var(--spacingHorizontalXS); + min-height: 24px; + min-width: 64px; + padding: 0 var(--spacingHorizontalS); + border-radius: var(--borderRadiusSmall); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + font-weight: var(--fontWeightRegular); +} + +:host([size='small'][icon-only]) { + min-width: 24px; + max-width: 24px; +} + +:host([size='large']) { + min-height: 40px; + border-radius: var(--borderRadiusLarge); + padding: 0 var(--spacingHorizontalL); + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} + +:host([size='large'][icon-only]) { + min-width: 40px; + max-width: 40px; +} + +:host([size='large']) ::slotted(svg) { + font-size: 24px; + height: 24px; + width: 24px; +} + +:host(:is([shape='circular'], [shape='circular']:focus-visible)) { + border-radius: var(--borderRadiusCircular); +} + +:host(:is([shape='square'], [shape='square']:focus-visible)) { + border-radius: var(--borderRadiusNone); +} + +:host([appearance='primary']) { + background-color: var(--colorBrandBackground); + color: var(--colorNeutralForegroundOnBrand); + border-color: transparent; +} + +:host([appearance='primary']:hover) { + background-color: var(--colorBrandBackgroundHover); +} + +:host([appearance='primary']:is(:hover, :hover:active):not(:focus-visible)) { + border-color: transparent; +} + +:host([appearance='primary']:is(:hover, :hover:active)) { + color: var(--colorNeutralForegroundOnBrand); +} + +:host([appearance='primary']:hover:active) { + background-color: var(--colorBrandBackgroundPressed); +} + +:host([appearance='primary']:focus-visible) { + border-color: var(--colorNeutralForegroundOnBrand); + box-shadow: var(--shadow2), 0 0 0 2px var(--colorStrokeFocus2); +} + +:host([appearance='outline']) { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='outline']:hover) { + background-color: var(--colorTransparentBackgroundHover); +} + +:host([appearance='outline']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); +} + +:host([appearance='subtle']) { + background-color: var(--colorSubtleBackground); + color: var(--colorNeutralForeground2); + border-color: transparent; +} + +:host([appearance='subtle']:hover) { + background-color: var(--colorSubtleBackgroundHover); + color: var(--colorNeutralForeground2Hover); + border-color: transparent; +} + +:host([appearance='subtle']:hover:active) { + background-color: var(--colorSubtleBackgroundPressed); + color: var(--colorNeutralForeground2Pressed); + border-color: transparent; +} + +:host([appearance='subtle']:hover) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='subtle']:hover:active) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandPressed); +} + +:host([appearance='transparent']) { + background-color: var(--colorTransparentBackground); + color: var(--colorNeutralForeground2); +} + +:host([appearance='transparent']:hover) { + background-color: var(--colorTransparentBackgroundHover); + color: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='transparent']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); + color: var(--colorNeutralForeground2BrandPressed); +} + +:host(:is([appearance='transparent'], [appearance='transparent']:is(:hover, :active))) { + border-color: transparent; +} + +::slotted(a) { + position: absolute; + inset: 0; +} + +@media (forced-colors: active) { + :host { + border-color: LinkText; + color: LinkText; + } +} diff --git a/packages/web-components/src/anchor-button/anchor-button.template.html b/packages/web-components/src/anchor-button/anchor-button.template.html new file mode 100644 index 0000000000000..4047a190152bb --- /dev/null +++ b/packages/web-components/src/anchor-button/anchor-button.template.html @@ -0,0 +1,10 @@ + + + diff --git a/packages/web-components/src/avatar/avatar.styles.css b/packages/web-components/src/avatar/avatar.styles.css new file mode 100644 index 0000000000000..1affb564412a4 --- /dev/null +++ b/packages/web-components/src/avatar/avatar.styles.css @@ -0,0 +1,480 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-grid; +} +:host { + position: relative; + place-items: center; + place-content: center; + grid-template: 1fr / 1fr; + flex-shrink: 0; + width: 32px; + height: 32px; + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + font-size: var(--fontSizeBase300); + border-radius: var(--borderRadiusCircular); + color: var(--colorNeutralForeground3); + background-color: var(--colorNeutralBackground6); + contain: layout style; +} + +.monogram, +.default-icon { + grid-area: 1 / 1 / -1 / -1; +} + +.monogram:empty { + display: none; +} + +.default-slot:is(.has-slotted, :has-slotted) ~ .default-icon, +.default-slot:is(.has-slotted, :has-slotted) ~ .monogram, +:host(:is([name]):not([name=''])) .default-icon, +:host(:is([initials]):not([initials=''])) .default-icon { + display: none; +} + +.default-icon, +::slotted(svg) { + width: 20px; + height: 20px; + font-size: 20px; +} + +::slotted(img) { + box-sizing: border-box; + width: 100%; + height: 100%; + border-radius: var(--borderRadiusCircular); +} + +::slotted([slot='badge']) { + position: absolute; + bottom: 0; + right: 0; + box-shadow: 0 0 0 var(--strokeWidthThin) var(--colorNeutralBackground1); +} + +:host([size='64']) ::slotted([slot='badge']), +:host([size='72']) ::slotted([slot='badge']), +:host([size='96']) ::slotted([slot='badge']), +:host([size='120']) ::slotted([slot='badge']), +:host([size='128']) ::slotted([slot='badge']) { + box-shadow: 0 0 0 var(--strokeWidthThick) var(--colorNeutralBackground1); +} + +:host([size='16']), +:host([size='20']), +:host([size='24']) { + font-size: var(--fontSizeBase100); + font-weight: var(--fontWeightRegular); +} + +:host([size='16']) { + width: 16px; + height: 16px; +} + +:host([size='20']) { + width: 20px; + height: 20px; +} + +:host([size='24']) { + width: 24px; + height: 24px; +} + +:host([size='16']) .default-icon, +:host([size='16']) ::slotted(svg) { + width: 12px; + height: 12px; + font-size: 12px; +} + +:host([size='20']) .default-icon, +:host([size='24']) .default-icon, +:host([size='20']) ::slotted(svg), +:host([size='24']) ::slotted(svg) { + width: 16px; + height: 16px; + font-size: 16px; +} + +:host([size='28']) { + width: 28px; + height: 28px; + font-size: var(--fontSizeBase200); +} + +:host([size='36']) { + width: 36px; + height: 36px; +} + +:host([size='40']) { + width: 40px; + height: 40px; +} + +:host([size='48']), +:host([size='56']) { + font-size: var(--fontSizeBase400); +} + +:host([size='48']) { + width: 48px; + height: 48px; +} + +:host([size='48']) .default-icon, +:host([size='48']) ::slotted(svg) { + width: 24px; + height: 24px; + font-size: 24px; +} + +:host([size='56']) { + width: 56px; + height: 56px; +} + +:host([size='56']) .default-icon, +:host([size='56']) ::slotted(svg) { + width: 28px; + height: 28px; + font-size: 28px; +} + +:host([size='64']), +:host([size='72']), +:host([size='96']) { + font-size: var(--fontSizeBase500); +} + +:host([size='64']) .default-icon, +:host([size='72']) .default-icon, +:host([size='64']) ::slotted(svg), +:host([size='72']) ::slotted(svg) { + width: 32px; + height: 32px; + font-size: 32px; +} + +:host([size='64']) { + width: 64px; + height: 64px; +} + +:host([size='72']) { + width: 72px; + height: 72px; +} + +:host([size='96']) { + width: 96px; + height: 96px; +} + +:host([size='96']) .default-icon, +:host([size='120']) .default-icon, +:host([size='128']) .default-icon, +:host([size='96']) ::slotted(svg), +:host([size='120']) ::slotted(svg), +:host([size='128']) ::slotted(svg) { + width: 48px; + height: 48px; + font-size: 48px; +} + +:host([size='120']), +:host([size='128']) { + font-size: var(--fontSizeBase600); +} + +:host([size='120']) { + width: 120px; + height: 120px; +} + +:host([size='128']) { + width: 128px; + height: 128px; +} + +:host([shape='square']) { + border-radius: var(--borderRadiusMedium); +} + +:host([shape='square'][size='20']), +:host([shape='square'][size='24']) { + border-radius: var(--borderRadiusSmall); +} + +:host([shape='square'][size='56']), +:host([shape='square'][size='64']), +:host([shape='square'][size='72']) { + border-radius: var(--borderRadiusLarge); +} +:host([shape='square'][size='96']), +:host([shape='square'][size='120']), +:host([shape='square'][size='128']) { + border-radius: var(--borderRadiusXLarge); +} + +:host([data-color='brand']) { + color: var(--colorNeutralForegroundStaticInverted); + background-color: var(--colorBrandBackgroundStatic); +} + +:host([data-color='dark-red']) { + color: var(--colorPaletteDarkRedForeground2); + background-color: var(--colorPaletteDarkRedBackground2); +} + +:host([data-color='cranberry']) { + color: var(--colorPaletteCranberryForeground2); + background-color: var(--colorPaletteCranberryBackground2); +} + +:host([data-color='red']) { + color: var(--colorPaletteRedForeground2); + background-color: var(--colorPaletteRedBackground2); +} + +:host([data-color='pumpkin']) { + color: var(--colorPalettePumpkinForeground2); + background-color: var(--colorPalettePumpkinBackground2); +} + +:host([data-color='peach']) { + color: var(--colorPalettePeachForeground2); + background-color: var(--colorPalettePeachBackground2); +} + +:host([data-color='marigold']) { + color: var(--colorPaletteMarigoldForeground2); + background-color: var(--colorPaletteMarigoldBackground2); +} + +:host([data-color='gold']) { + color: var(--colorPaletteGoldForeground2); + background-color: var(--colorPaletteGoldBackground2); +} + +:host([data-color='brass']) { + color: var(--colorPaletteBrassForeground2); + background-color: var(--colorPaletteBrassBackground2); +} + +:host([data-color='brown']) { + color: var(--colorPaletteBrownForeground2); + background-color: var(--colorPaletteBrownBackground2); +} + +:host([data-color='forest']) { + color: var(--colorPaletteForestForeground2); + background-color: var(--colorPaletteForestBackground2); +} + +:host([data-color='seafoam']) { + color: var(--colorPaletteSeafoamForeground2); + background-color: var(--colorPaletteSeafoamBackground2); +} + +:host([data-color='dark-green']) { + color: var(--colorPaletteDarkGreenForeground2); + background-color: var(--colorPaletteDarkGreenBackground2); +} + +:host([data-color='light-teal']) { + color: var(--colorPaletteLightTealForeground2); + background-color: var(--colorPaletteLightTealBackground2); +} + +:host([data-color='teal']) { + color: var(--colorPaletteTealForeground2); + background-color: var(--colorPaletteTealBackground2); +} + +:host([data-color='steel']) { + color: var(--colorPaletteSteelForeground2); + background-color: var(--colorPaletteSteelBackground2); +} + +:host([data-color='blue']) { + color: var(--colorPaletteBlueForeground2); + background-color: var(--colorPaletteBlueBackground2); +} + +:host([data-color='royal-blue']) { + color: var(--colorPaletteRoyalBlueForeground2); + background-color: var(--colorPaletteRoyalBlueBackground2); +} + +:host([data-color='cornflower']) { + color: var(--colorPaletteCornflowerForeground2); + background-color: var(--colorPaletteCornflowerBackground2); +} + +:host([data-color='navy']) { + color: var(--colorPaletteNavyForeground2); + background-color: var(--colorPaletteNavyBackground2); +} + +:host([data-color='lavender']) { + color: var(--colorPaletteLavenderForeground2); + background-color: var(--colorPaletteLavenderBackground2); +} + +:host([data-color='purple']) { + color: var(--colorPalettePurpleForeground2); + background-color: var(--colorPalettePurpleBackground2); +} + +:host([data-color='grape']) { + color: var(--colorPaletteGrapeForeground2); + background-color: var(--colorPaletteGrapeBackground2); +} + +:host([data-color='lilac']) { + color: var(--colorPaletteLilacForeground2); + background-color: var(--colorPaletteLilacBackground2); +} + +:host([data-color='pink']) { + color: var(--colorPalettePinkForeground2); + background-color: var(--colorPalettePinkBackground2); +} + +:host([data-color='magenta']) { + color: var(--colorPaletteMagentaForeground2); + background-color: var(--colorPaletteMagentaBackground2); +} + +:host([data-color='plum']) { + color: var(--colorPalettePlumForeground2); + background-color: var(--colorPalettePlumBackground2); +} + +:host([data-color='beige']) { + color: var(--colorPaletteBeigeForeground2); + background-color: var(--colorPaletteBeigeBackground2); +} + +:host([data-color='mink']) { + color: var(--colorPaletteMinkForeground2); + background-color: var(--colorPaletteMinkBackground2); +} + +:host([data-color='platinum']) { + color: var(--colorPalettePlatinumForeground2); + background-color: var(--colorPalettePlatinumBackground2); +} + +:host([data-color='anchor']) { + color: var(--colorPaletteAnchorForeground2); + background-color: var(--colorPaletteAnchorBackground2); +} + +:host([active]) { + /* Work-around for text pixel snapping at the end of the animation */ + transform: perspective(1px); + transition-property: transform, opacity; + transition-duration: var(--durationUltraSlow), var(--durationFaster); + transition-delay: var(--curveEasyEaseMax), var(--curveLinear); +} + +:host([active])::before { + content: ''; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + border-radius: inherit; + transition-property: margin, opacity; + transition-duration: var(--durationUltraSlow), var(--durationSlower); + transition-delay: var(--curveEasyEaseMax), var(--curveLinear); +} +:host([active])::before { + box-shadow: var(--shadow8); + border-style: solid; + border-color: var(--colorBrandBackgroundStatic); +} + +:host([active][appearance='shadow'])::before { + border-style: none; + border-color: none; +} + +:host([active]:not([appearance='shadow']))::before { + margin: calc(-2 * var(--strokeWidthThick)); + border-width: var(--strokeWidthThick); +} + +:host([size='56'][active]:not([appearance='shadow']))::before, +:host([size='64'][active]:not([appearance='shadow']))::before { + margin: calc(-2 * var(--strokeWidthThicker)); + border-width: var(--strokeWidthThicker); +} + +:host([size='72'][active]:not([appearance='shadow']))::before, +:host([size='96'][active]:not([appearance='shadow']))::before, +:host([size='120'][active]:not([appearance='shadow']))::before, +:host([size='128'][active]:not([appearance='shadow']))::before { + margin: calc(-2 * var(--strokeWidthThickest)); + border-width: var(--strokeWidthThickest); +} + +:host([size='20'][active][appearance])::before, +:host([size='24'][active][appearance])::before, +:host([size='28'][active][appearance])::before { + box-shadow: var(--shadow4); +} + +:host([size='56'][active][appearance])::before, +:host([size='64'][active][appearance])::before { + box-shadow: var(--shadow16); +} + +:host([size='72'][active][appearance])::before, +:host([size='96'][active][appearance])::before, +:host([size='120'][active][appearance])::before, +:host([size='128'][active][appearance])::before { + box-shadow: var(--shadow28); +} + +:host([active][appearance='ring'])::before { + box-shadow: none; +} + +:host([active='inactive']) { + opacity: 0.8; + transform: scale(0.875); + transition-property: transform, opacity; + transition-duration: var(--durationUltraSlow), var(--durationFaster); + transition-delay: var(--curveDecelerateMin), var(--curveLinear); +} + +:host([active='inactive'])::before { + margin: 0; + opacity: 0; + transition-property: margin, opacity; + transition-duration: var(--durationUltraSlow), var(--durationSlower); + transition-delay: var(--curveDecelerateMin), var(--curveLinear); +} + +@media screen and (prefers-reduced-motion: reduce) { + :host([active]) { + transition-duration: 0.01ms; + } + + :host([active])::before { + transition-duration: 0.01ms; + transition-delay: 0.01ms; + } +} diff --git a/packages/web-components/src/avatar/avatar.template.html b/packages/web-components/src/avatar/avatar.template.html new file mode 100644 index 0000000000000..96b118e4a5b2d --- /dev/null +++ b/packages/web-components/src/avatar/avatar.template.html @@ -0,0 +1,13 @@ + + + diff --git a/packages/web-components/src/badge/badge.styles.css b/packages/web-components/src/badge/badge.styles.css new file mode 100644 index 0000000000000..c5a21814f8b41 --- /dev/null +++ b/packages/web-components/src/badge/badge.styles.css @@ -0,0 +1,266 @@ +:host([shape='square']) { + border-radius: var(--borderRadiusNone); +} + +:host([shape='rounded']) { + border-radius: var(--borderRadiusMedium); +} + +:host([shape='rounded']:is([size='tiny'], [size='extra-small'], [size='small'])) { + border-radius: var(--borderRadiusSmall); +} + +:host([appearance='tint']) { + background-color: var(--colorBrandBackground2); + color: var(--colorBrandForeground2); + border-color: var(--colorBrandStroke2); +} + +:host([appearance='tint'][color='danger']) { + background-color: var(--colorPaletteRedBackground1); + color: var(--colorPaletteRedForeground1); + border-color: var(--colorPaletteRedBorder1); +} + +:host([appearance='tint'][color='important']) { + background-color: var(--colorNeutralForeground3); + color: var(--colorNeutralBackground1); + border-color: var(--colorTransparentStroke); +} + +:host([appearance='tint'][color='informative']) { + background-color: var(--colorNeutralBackground4); + color: var(--colorNeutralForeground3); + border-color: var(--colorNeutralStroke2); +} + +:host([appearance='tint'][color='severe']) { + background-color: var(--colorPaletteDarkOrangeBackground1); + color: var(--colorPaletteDarkOrangeForeground1); + border-color: var(--colorPaletteDarkOrangeBorder1); +} + +:host([appearance='tint'][color='subtle']) { + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground3); + border-color: var(--colorNeutralStroke2); +} + +:host([appearance='tint'][color='success']) { + background-color: var(--colorPaletteGreenBackground1); + color: var(--colorPaletteGreenForeground1); + border-color: var(--colorPaletteGreenBorder2); +} + +:host([appearance='tint'][color='warning']) { + background-color: var(--colorPaletteYellowBackground1); + color: var(--colorPaletteYellowForeground2); + border-color: var(--colorPaletteYellowBorder1); +} + +:host([appearance='outline']) { + border-color: currentColor; + color: var(--colorBrandForeground1); + background-color: initial; +} + +:host([appearance='outline'][color='danger']) { + color: var(--colorPaletteRedForeground3); +} + +:host([appearance='outline'][color='important']) { + color: var(--colorNeutralForeground3); + border-color: var(--colorNeutralStrokeAccessible); +} + +:host([appearance='outline'][color='informative']) { + color: var(--colorNeutralForeground3); + border-color: var(--colorNeutralStroke2); +} + +:host([appearance='outline'][color='severe']) { + color: var(--colorPaletteDarkOrangeForeground3); +} + +:host([appearance='outline'][color='subtle']) { + color: var(--colorNeutralForegroundStaticInverted); +} + +:host([appearance='outline'][color='success']) { + color: var(--colorPaletteGreenForeground2); +} + +:host([appearance='outline'][color='warning']) { + color: var(--colorPaletteYellowForeground2); +} + +:host([appearance='ghost']) { + color: var(--colorBrandForeground1); + background-color: initial; +} + +:host([appearance='ghost'][color='danger']) { + color: var(--colorPaletteRedForeground3); +} + +:host([appearance='ghost'][color='important']) { + color: var(--colorNeutralForeground1); +} + +:host([appearance='ghost'][color='informative']) { + color: var(--colorNeutralForeground3); +} + +:host([appearance='ghost'][color='severe']) { + color: var(--colorPaletteDarkOrangeForeground3); +} + +:host([appearance='ghost'][color='subtle']) { + color: var(--colorNeutralForegroundInverted); +} + +:host([appearance='ghost'][color='success']) { + color: var(--colorPaletteGreenForeground3); +} + +:host([appearance='ghost'][color='warning']) { + color: var(--colorPaletteYellowForeground2); +} + +:host([color='danger']) { + background-color: var(--colorPaletteRedBackground3); + color: var(--colorNeutralForegroundOnBrand); +} + +:host([color='important']) { + background-color: var(--colorNeutralForeground1); + color: var(--colorNeutralBackground1); +} + +:host([color='informative']) { + background-color: var(--colorNeutralBackground5); + color: var(--colorNeutralForeground3); +} + +:host([color='severe']) { + background-color: var(--colorPaletteDarkOrangeBackground3); + color: var(--colorNeutralForegroundOnBrand); +} + +:host([color='subtle']) { + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground1); +} + +:host([color='success']) { + background-color: var(--colorPaletteGreenBackground3); + color: var(--colorNeutralForegroundOnBrand); +} + +:host([color='warning']) { + background-color: var(--colorPaletteYellowBackground3); + color: var(--colorNeutralForeground1Static); +} + +:host([size='tiny']) { + width: 6px; + height: 6px; + font-size: 4px; + line-height: 4px; + padding-inline: 0; + min-width: unset; +} +:host([size='tiny']) ::slotted(svg) { + font-size: 6px; +} +:host([size='extra-small']) { + width: 10px; + height: 10px; + font-size: 6px; + line-height: 6px; + padding-inline: 0; + min-width: unset; +} +:host([size='extra-small']) ::slotted(svg) { + font-size: 10px; +} +:host([size='small']) { + min-width: 16px; + height: 16px; + font-size: var(--fontSizeBase100); + line-height: var(--lineHeightBase100); + padding-inline: calc(var(--spacingHorizontalXXS) + var(--spacingHorizontalXXS)); +} +:host([size='small']) ::slotted(svg) { + font-size: 12px; +} +:host([size='large']) { + min-width: 24px; + height: 24px; + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + padding-inline: calc(var(--spacingHorizontalXS) + var(--spacingHorizontalXXS)); +} +:host([size='large']) ::slotted(svg) { + font-size: 16px; +} +:host([size='extra-large']) { + min-width: 32px; + height: 32px; + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + padding-inline: calc(var(--spacingHorizontalSNudge) + var(--spacingHorizontalXXS)); +} +:host([size='extra-large']) ::slotted(svg) { + font-size: 20px; +} + +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} +:host { + position: relative; + box-sizing: border-box; + align-items: center; + justify-content: center; + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + min-width: 20px; + height: 20px; + padding-inline: calc(var(--spacingHorizontalXS) + var(--spacingHorizontalXXS)); + border-radius: var(--borderRadiusCircular); + border-color: var(--colorTransparentStroke); + background-color: var(--colorBrandBackground); + color: var(--colorNeutralForegroundOnBrand); + contain: content; +} + +::slotted(svg) { + font-size: 12px; +} + +:host(:not([appearance='ghost']))::after { + position: absolute; + content: ''; + top: 0; + left: 0; + bottom: 0; + right: 0; + border-style: solid; + border-width: var(--strokeWidthThin); + border-color: inherit; + border-radius: inherit; +} + +@media (forced-colors: active) { + :host, + :host([appearance='outline']), + :host([appearance='tint']) { + border-color: CanvasText; + } +} diff --git a/packages/web-components/src/badge/badge.template.html b/packages/web-components/src/badge/badge.template.html new file mode 100644 index 0000000000000..e7d9cc36640ed --- /dev/null +++ b/packages/web-components/src/badge/badge.template.html @@ -0,0 +1,8 @@ + + + diff --git a/packages/web-components/src/button/button.styles.css b/packages/web-components/src/button/button.styles.css new file mode 100644 index 0000000000000..15bddfefdbf8a --- /dev/null +++ b/packages/web-components/src/button/button.styles.css @@ -0,0 +1,268 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + --icon-spacing: var(--spacingHorizontalSNudge); + position: relative; + contain: layout style; + vertical-align: middle; + align-items: center; + box-sizing: border-box; + justify-content: center; + text-align: center; + text-decoration-line: none; + margin: 0; + min-height: 32px; + outline-style: none; + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground1); + border: var(--strokeWidthThin) solid var(--colorNeutralStroke1); + padding: 0 var(--spacingHorizontalM); + min-width: 96px; + border-radius: var(--borderRadiusMedium); + font-size: var(--fontSizeBase300); + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + line-height: var(--lineHeightBase300); + transition-duration: var(--durationFaster); + transition-property: background, border, color; + transition-timing-function: var(--curveEasyEase); + cursor: pointer; + user-select: none; +} + +.content { + display: inherit; +} + +:host(:hover) { + background-color: var(--colorNeutralBackground1Hover); + color: var(--colorNeutralForeground1Hover); + border-color: var(--colorNeutralStroke1Hover); +} + +:host(:hover:active) { + background-color: var(--colorNeutralBackground1Pressed); + border-color: var(--colorNeutralStroke1Pressed); + color: var(--colorNeutralForeground1Pressed); + outline-style: none; +} + +:host(:focus-visible) { + border-color: var(--colorTransparentStroke); + outline: var(--strokeWidthThick) solid var(--colorTransparentStroke); + box-shadow: var(--shadow4), 0 0 0 2px var(--colorStrokeFocus2); +} + +@media screen and (prefers-reduced-motion: reduce) { + :host { + transition-duration: 0.01ms; + } +} + +::slotted(svg) { + font-size: 20px; + height: 20px; + width: 20px; + fill: currentColor; +} + +::slotted([slot='start']) { + margin-inline-end: var(--icon-spacing); +} + +::slotted([slot='end']), +[slot='end'] { + flex-shrink: 0; + margin-inline-start: var(--icon-spacing); +} + +:host([icon-only]) { + min-width: 32px; + max-width: 32px; +} + +:host([size='small']) { + --icon-spacing: var(--spacingHorizontalXS); + min-height: 24px; + min-width: 64px; + padding: 0 var(--spacingHorizontalS); + border-radius: var(--borderRadiusSmall); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + font-weight: var(--fontWeightRegular); +} + +:host([size='small'][icon-only]) { + min-width: 24px; + max-width: 24px; +} + +:host([size='large']) { + min-height: 40px; + border-radius: var(--borderRadiusLarge); + padding: 0 var(--spacingHorizontalL); + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} + +:host([size='large'][icon-only]) { + min-width: 40px; + max-width: 40px; +} + +:host([size='large']) ::slotted(svg) { + font-size: 24px; + height: 24px; + width: 24px; +} + +:host(:is([shape='circular'], [shape='circular']:focus-visible)) { + border-radius: var(--borderRadiusCircular); +} + +:host(:is([shape='square'], [shape='square']:focus-visible)) { + border-radius: var(--borderRadiusNone); +} + +:host([appearance='primary']) { + background-color: var(--colorBrandBackground); + color: var(--colorNeutralForegroundOnBrand); + border-color: transparent; +} + +:host([appearance='primary']:hover) { + background-color: var(--colorBrandBackgroundHover); +} + +:host([appearance='primary']:is(:hover, :hover:active):not(:focus-visible)) { + border-color: transparent; +} + +:host([appearance='primary']:is(:hover, :hover:active)) { + color: var(--colorNeutralForegroundOnBrand); +} + +:host([appearance='primary']:hover:active) { + background-color: var(--colorBrandBackgroundPressed); +} + +:host([appearance='primary']:focus-visible) { + border-color: var(--colorNeutralForegroundOnBrand); + box-shadow: var(--shadow2), 0 0 0 2px var(--colorStrokeFocus2); +} + +:host([appearance='outline']) { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='outline']:hover) { + background-color: var(--colorTransparentBackgroundHover); +} + +:host([appearance='outline']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); +} + +:host([appearance='subtle']) { + background-color: var(--colorSubtleBackground); + color: var(--colorNeutralForeground2); + border-color: transparent; +} + +:host([appearance='subtle']:hover) { + background-color: var(--colorSubtleBackgroundHover); + color: var(--colorNeutralForeground2Hover); + border-color: transparent; +} + +:host([appearance='subtle']:hover:active) { + background-color: var(--colorSubtleBackgroundPressed); + color: var(--colorNeutralForeground2Pressed); + border-color: transparent; +} + +:host([appearance='subtle']:hover) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='subtle']:hover:active) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandPressed); +} + +:host([appearance='transparent']) { + background-color: var(--colorTransparentBackground); + color: var(--colorNeutralForeground2); +} + +:host([appearance='transparent']:hover) { + background-color: var(--colorTransparentBackgroundHover); + color: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='transparent']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); + color: var(--colorNeutralForeground2BrandPressed); +} + +:host(:is([appearance='transparent'], [appearance='transparent']:is(:hover, :active))) { + border-color: transparent; +} + +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])), +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable]):hover), +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable]):hover:active) { + background-color: var(--colorNeutralBackgroundDisabled); + border-color: var(--colorNeutralStrokeDisabled); + color: var(--colorNeutralForegroundDisabled); + cursor: not-allowed; +} + +:host([appearance='primary']:is(:disabled, [disabled-focusable])), +:host([appearance='primary']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + border-color: transparent; +} + +:host([appearance='outline']:is(:disabled, [disabled-focusable])), +:host([appearance='outline']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='subtle']:is(:disabled, [disabled-focusable])), +:host([appearance='subtle']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + background-color: var(--colorTransparentBackground); + border-color: transparent; +} + +:host([appearance='transparent']:is(:disabled, [disabled-focusable])), +:host([appearance='transparent']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + border-color: transparent; + background-color: var(--colorTransparentBackground); +} + +@media (forced-colors: active) { + :host { + background-color: ButtonFace; + color: ButtonText; + } + + :host(:is(:hover, :focus-visible)) { + border-color: Highlight !important; + } + + :host([appearance='primary']:not(:is(:hover, :focus-visible))) { + background-color: Highlight; + color: HighlightText; + forced-color-adjust: none; + } + + :host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])) { + background-color: ButtonFace; + color: GrayText; + border-color: ButtonText; + } +} diff --git a/packages/web-components/src/button/button.template.html b/packages/web-components/src/button/button.template.html new file mode 100644 index 0000000000000..a9e479995cda8 --- /dev/null +++ b/packages/web-components/src/button/button.template.html @@ -0,0 +1,10 @@ + + + diff --git a/packages/web-components/src/checkbox/checkbox.styles.css b/packages/web-components/src/checkbox/checkbox.styles.css new file mode 100644 index 0000000000000..8ebe81e852007 --- /dev/null +++ b/packages/web-components/src/checkbox/checkbox.styles.css @@ -0,0 +1,172 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + --size: 16px; + background-color: var(--colorNeutralBackground1); + border-radius: var(--borderRadiusSmall); + border: var(--strokeWidthThin) solid var(--colorNeutralStrokeAccessible); + box-sizing: border-box; + cursor: pointer; + position: relative; + width: var(--size); +} + +:host, +.indeterminate-indicator, +.checked-indicator { + aspect-ratio: 1; +} + +:host(:hover) { + border-color: var(--colorNeutralStrokeAccessibleHover); +} + +:host(:active) { + border-color: var(--colorNeutralStrokeAccessiblePressed); +} + +:host(:state(checked):hover) { + background-color: var(--colorCompoundBrandBackgroundHover); + border-color: var(--colorCompoundBrandStrokeHover); +} + +:host(:state(checked):active) { + background-color: var(--colorCompoundBrandBackgroundPressed); + border-color: var(--colorCompoundBrandStrokePressed); +} + +:host(:focus-visible) { + outline: none; +} + +:host(:not([slot='input']))::after { + content: ''; + position: absolute; + inset: -8px; + box-sizing: border-box; + outline: none; + border: var(--strokeWidthThick) solid var(--colorTransparentStroke); + border-radius: var(--borderRadiusMedium); +} + +:host(:not([slot='input']):focus-visible)::after { + border-color: var(--colorStrokeFocus2); +} + +.indeterminate-indicator, +.checked-indicator { + color: var(--colorNeutralForegroundInverted); + inset: 0; + margin: auto; + position: absolute; +} + +::slotted([slot='checked-indicator']), +.checked-indicator { + fill: currentColor; + display: inline-flex; + flex: 1 0 auto; + width: 12px; +} + +:host(:not(:state(checked))) *:is(::slotted([slot='checked-indicator']), .checked-indicator) { + display: none; +} + +:host(:state(checked)), +:host(:state(indeterminate)) { + border-color: var(--colorCompoundBrandStroke); +} + +:host(:state(checked)), +:host(:state(indeterminate)) .indeterminate-indicator { + background-color: var(--colorCompoundBrandBackground); +} + +:host(:state(indeterminate)) .indeterminate-indicator { + border-radius: var(--borderRadiusSmall); + position: absolute; + width: calc(var(--size) / 2); + inset: 0; +} + +:host([size='large']) { + --size: 20px; +} + +:host([size='large']) ::slotted([slot='checked-indicator']), +:host([size='large']) .checked-indicator { + width: 16px; +} + +:host([shape='circular']), +:host([shape='circular']) .indeterminate-indicator { + border-radius: var(--borderRadiusCircular); +} + +:host([disabled]), +:host([disabled]:state(checked)) { + background-color: var(--colorNeutralBackgroundDisabled); + border-color: var(--colorNeutralStrokeDisabled); +} + +:host([disabled]) { + cursor: unset; +} + +:host([disabled]:state(indeterminate)) .indeterminate-indicator { + background-color: var(--colorNeutralStrokeDisabled); +} + +:host([disabled]:state(checked)) .checked-indicator { + color: var(--colorNeutralStrokeDisabled); +} + +@media (forced-colors: active) { + :host { + border-color: FieldText; + } + + :host(:not([slot='input']:focus-visible))::after { + border-color: Canvas; + } + + :host(:not([disabled]):hover), + :host(:state(checked):not([disabled]):hover), + :host(:not([slot='input']):focus-visible)::after { + border-color: Highlight; + } + + .indeterminate-indicator, + .checked-indicator { + color: HighlightText; + } + + :host(:state(checked)), + :host(:state(indeterminate)) .indeterminate-indicator { + background-color: FieldText; + } + + :host(:state(checked):not([disabled]):hover), + :host(:state(indeterminate):not([disabled]):hover) .indeterminate-indicator { + background-color: Highlight; + } + + :host([disabled]) { + border-color: GrayText; + } + + :host([disabled]:state(indeterminate)) .indeterminate-indicator { + background-color: GrayText; + } + + :host([disabled]), + :host([disabled]:state(checked)) .checked-indicator { + color: GrayText; + } +} diff --git a/packages/web-components/src/checkbox/checkbox.template.html b/packages/web-components/src/checkbox/checkbox.template.html new file mode 100644 index 0000000000000..4300eb1e7afc4 --- /dev/null +++ b/packages/web-components/src/checkbox/checkbox.template.html @@ -0,0 +1,29 @@ + + + diff --git a/packages/web-components/src/compound-button/compound-button.styles.css b/packages/web-components/src/compound-button/compound-button.styles.css new file mode 100644 index 0000000000000..5efe49e318540 --- /dev/null +++ b/packages/web-components/src/compound-button/compound-button.styles.css @@ -0,0 +1,370 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + --icon-spacing: var(--spacingHorizontalSNudge); + position: relative; + contain: layout style; + vertical-align: middle; + align-items: center; + box-sizing: border-box; + justify-content: center; + text-align: center; + text-decoration-line: none; + margin: 0; + min-height: 32px; + outline-style: none; + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground1); + border: var(--strokeWidthThin) solid var(--colorNeutralStroke1); + padding: 0 var(--spacingHorizontalM); + min-width: 96px; + border-radius: var(--borderRadiusMedium); + font-size: var(--fontSizeBase300); + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + line-height: var(--lineHeightBase300); + transition-duration: var(--durationFaster); + transition-property: background, border, color; + transition-timing-function: var(--curveEasyEase); + cursor: pointer; + user-select: none; +} + +.content { + display: inherit; +} + +:host(:hover) { + background-color: var(--colorNeutralBackground1Hover); + color: var(--colorNeutralForeground1Hover); + border-color: var(--colorNeutralStroke1Hover); +} + +:host(:hover:active) { + background-color: var(--colorNeutralBackground1Pressed); + border-color: var(--colorNeutralStroke1Pressed); + color: var(--colorNeutralForeground1Pressed); + outline-style: none; +} + +:host(:focus-visible) { + border-color: var(--colorTransparentStroke); + outline: var(--strokeWidthThick) solid var(--colorTransparentStroke); + box-shadow: var(--shadow4), 0 0 0 2px var(--colorStrokeFocus2); +} + +@media screen and (prefers-reduced-motion: reduce) { + :host { + transition-duration: 0.01ms; + } +} + +::slotted(svg) { + font-size: 20px; + height: 20px; + width: 20px; + fill: currentColor; +} + +::slotted([slot='start']) { + margin-inline-end: var(--icon-spacing); +} + +::slotted([slot='end']), +[slot='end'] { + flex-shrink: 0; + margin-inline-start: var(--icon-spacing); +} + +:host([icon-only]) { + min-width: 32px; + max-width: 32px; +} + +:host([size='small']) { + --icon-spacing: var(--spacingHorizontalXS); + min-height: 24px; + min-width: 64px; + padding: 0 var(--spacingHorizontalS); + border-radius: var(--borderRadiusSmall); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + font-weight: var(--fontWeightRegular); +} + +:host([size='small'][icon-only]) { + min-width: 24px; + max-width: 24px; +} + +:host([size='large']) { + min-height: 40px; + border-radius: var(--borderRadiusLarge); + padding: 0 var(--spacingHorizontalL); + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} + +:host([size='large'][icon-only]) { + min-width: 40px; + max-width: 40px; +} + +:host([size='large']) ::slotted(svg) { + font-size: 24px; + height: 24px; + width: 24px; +} + +:host(:is([shape='circular'], [shape='circular']:focus-visible)) { + border-radius: var(--borderRadiusCircular); +} + +:host(:is([shape='square'], [shape='square']:focus-visible)) { + border-radius: var(--borderRadiusNone); +} + +:host([appearance='primary']) { + background-color: var(--colorBrandBackground); + color: var(--colorNeutralForegroundOnBrand); + border-color: transparent; +} + +:host([appearance='primary']:hover) { + background-color: var(--colorBrandBackgroundHover); +} + +:host([appearance='primary']:is(:hover, :hover:active):not(:focus-visible)) { + border-color: transparent; +} + +:host([appearance='primary']:is(:hover, :hover:active)) { + color: var(--colorNeutralForegroundOnBrand); +} + +:host([appearance='primary']:hover:active) { + background-color: var(--colorBrandBackgroundPressed); +} + +:host([appearance='primary']:focus-visible) { + border-color: var(--colorNeutralForegroundOnBrand); + box-shadow: var(--shadow2), 0 0 0 2px var(--colorStrokeFocus2); +} + +:host([appearance='outline']) { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='outline']:hover) { + background-color: var(--colorTransparentBackgroundHover); +} + +:host([appearance='outline']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); +} + +:host([appearance='subtle']) { + background-color: var(--colorSubtleBackground); + color: var(--colorNeutralForeground2); + border-color: transparent; +} + +:host([appearance='subtle']:hover) { + background-color: var(--colorSubtleBackgroundHover); + color: var(--colorNeutralForeground2Hover); + border-color: transparent; +} + +:host([appearance='subtle']:hover:active) { + background-color: var(--colorSubtleBackgroundPressed); + color: var(--colorNeutralForeground2Pressed); + border-color: transparent; +} + +:host([appearance='subtle']:hover) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='subtle']:hover:active) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandPressed); +} + +:host([appearance='transparent']) { + background-color: var(--colorTransparentBackground); + color: var(--colorNeutralForeground2); +} + +:host([appearance='transparent']:hover) { + background-color: var(--colorTransparentBackgroundHover); + color: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='transparent']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); + color: var(--colorNeutralForeground2BrandPressed); +} + +:host(:is([appearance='transparent'], [appearance='transparent']:is(:hover, :active))) { + border-color: transparent; +} + +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])), +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable]):hover), +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable]):hover:active) { + background-color: var(--colorNeutralBackgroundDisabled); + border-color: var(--colorNeutralStrokeDisabled); + color: var(--colorNeutralForegroundDisabled); + cursor: not-allowed; +} + +:host([appearance='primary']:is(:disabled, [disabled-focusable])), +:host([appearance='primary']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + border-color: transparent; +} + +:host([appearance='outline']:is(:disabled, [disabled-focusable])), +:host([appearance='outline']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='subtle']:is(:disabled, [disabled-focusable])), +:host([appearance='subtle']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + background-color: var(--colorTransparentBackground); + border-color: transparent; +} + +:host([appearance='transparent']:is(:disabled, [disabled-focusable])), +:host([appearance='transparent']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + border-color: transparent; + background-color: var(--colorTransparentBackground); +} + +@media (forced-colors: active) { + :host { + background-color: ButtonFace; + color: ButtonText; + } + + :host(:is(:hover, :focus-visible)) { + border-color: Highlight !important; + } + + :host([appearance='primary']:not(:is(:hover, :focus-visible))) { + background-color: Highlight; + color: HighlightText; + forced-color-adjust: none; + } + + :host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])) { + background-color: ButtonFace; + color: GrayText; + border-color: ButtonText; + } +} + +:host, +:host(:is([size])) { + gap: 12px; + height: auto; + padding-top: 14px; + padding-inline: 12px; + padding-bottom: 16px; + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); +} + +.content { + display: flex; + flex-direction: column; + text-align: start; +} + +::slotted([slot='description']) { + color: var(--colorNeutralForeground2); + line-height: 100%; + font-size: var(--fontSizeBase200); + font-weight: var(--fontWeightRegular); +} + +::slotted(svg), +:host([size='large']) ::slotted(svg) { + font-size: 40px; + height: 40px; + width: 40px; +} + +:host(:hover) ::slotted([slot='description']) { + color: var(--colorNeutralForeground2Hover); +} + +:host(:active) ::slotted([slot='description']) { + color: var(--colorNeutralForeground2Pressed); +} + +:host(:is([appearance='primary'], [appearance='primary']:is(:hover, :active))) ::slotted([slot='description']) { + color: var(--colorNeutralForegroundOnBrand); +} + +:host(:is([appearance='transparent'], [appearance='subtle'], [appearance='subtle']:is(:hover, :active))) + ::slotted([slot='description']) { + color: var(--colorNeutralForeground2); +} + +:host([appearance='transparent']:hover) ::slotted([slot='description']) { + color: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='transparent']:active) ::slotted([slot='description']) { + color: var(--colorNeutralForeground2BrandPressed); +} + +:host(:is(:disabled, :disabled[appearance], [disabled-focusable], [disabled-focusable][appearance])) + ::slotted([slot='description']) { + color: var(--colorNeutralForegroundDisabled); +} + +:host([size='small']) { + padding: 8px; + padding-bottom: 10px; +} + +:host([icon-only]) { + min-width: 52px; + max-width: 52px; + padding: var(--spacingHorizontalSNudge); +} + +:host([icon-only][size='small']) { + min-width: 48px; + max-width: 48px; + padding: var(--spacingHorizontalXS); +} + +:host([icon-only][size='large']) { + min-width: 56px; + max-width: 56px; + padding: var(--spacingHorizontalS); +} + +:host([size='large']) { + padding-top: 18px; + padding-inline: 16px; + padding-bottom: 20px; + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} +:host([size='large']) ::slotted([slot='description']) { + font-size: var(--fontSizeBase300); +} + +@media (forced-colors: active) { + :host([appearance='primary']:not(:hover, :focus-visible, :disabled, [disabled-focusable])) + ::slotted([slot='description']) { + color: HighlightText; + } +} diff --git a/packages/web-components/src/compound-button/compound-button.template.html b/packages/web-components/src/compound-button/compound-button.template.html new file mode 100644 index 0000000000000..51a90b0ff37b5 --- /dev/null +++ b/packages/web-components/src/compound-button/compound-button.template.html @@ -0,0 +1,11 @@ + + + diff --git a/packages/web-components/src/counter-badge/counter-badge.styles.css b/packages/web-components/src/counter-badge/counter-badge.styles.css new file mode 100644 index 0000000000000..f36391b6c850a --- /dev/null +++ b/packages/web-components/src/counter-badge/counter-badge.styles.css @@ -0,0 +1,177 @@ +:host([shape='rounded']) { + border-radius: var(--borderRadiusMedium); +} + +:host([shape='rounded']:is([size='tiny'], [size='extra-small'], [size='small'])) { + border-radius: var(--borderRadiusSmall); +} + +:host([size='tiny']) { + width: 6px; + height: 6px; + font-size: 4px; + line-height: 4px; + padding-inline: 0; + min-width: unset; +} +:host([size='tiny']) ::slotted(svg) { + font-size: 6px; +} +:host([size='extra-small']) { + width: 10px; + height: 10px; + font-size: 6px; + line-height: 6px; + padding-inline: 0; + min-width: unset; +} +:host([size='extra-small']) ::slotted(svg) { + font-size: 10px; +} +:host([size='small']) { + min-width: 16px; + height: 16px; + font-size: var(--fontSizeBase100); + line-height: var(--lineHeightBase100); + padding-inline: calc(var(--spacingHorizontalXXS) + var(--spacingHorizontalXXS)); +} +:host([size='small']) ::slotted(svg) { + font-size: 12px; +} +:host([size='large']) { + min-width: 24px; + height: 24px; + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + padding-inline: calc(var(--spacingHorizontalXS) + var(--spacingHorizontalXXS)); +} +:host([size='large']) ::slotted(svg) { + font-size: 16px; +} +:host([size='extra-large']) { + min-width: 32px; + height: 32px; + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + padding-inline: calc(var(--spacingHorizontalSNudge) + var(--spacingHorizontalXXS)); +} +:host([size='extra-large']) ::slotted(svg) { + font-size: 20px; +} + +:host([color='danger']) { + background-color: var(--colorPaletteRedBackground3); + color: var(--colorNeutralForegroundOnBrand); +} + +:host([color='important']) { + background-color: var(--colorNeutralForeground1); + color: var(--colorNeutralBackground1); +} + +:host([color='informative']) { + background-color: var(--colorNeutralBackground5); + color: var(--colorNeutralForeground3); +} + +:host([color='severe']) { + background-color: var(--colorPaletteDarkOrangeBackground3); + color: var(--colorNeutralForegroundOnBrand); +} + +:host([color='subtle']) { + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground1); +} + +:host([color='success']) { + background-color: var(--colorPaletteGreenBackground3); + color: var(--colorNeutralForegroundOnBrand); +} + +:host([color='warning']) { + background-color: var(--colorPaletteYellowBackground3); + color: var(--colorNeutralForeground1Static); +} + +:host([appearance='ghost']) { + color: var(--colorBrandForeground1); + background-color: initial; +} + +:host([appearance='ghost'][color='danger']) { + color: var(--colorPaletteRedForeground3); +} + +:host([appearance='ghost'][color='important']) { + color: var(--colorNeutralForeground1); +} + +:host([appearance='ghost'][color='informative']) { + color: var(--colorNeutralForeground3); +} + +:host([appearance='ghost'][color='severe']) { + color: var(--colorPaletteDarkOrangeForeground3); +} + +:host([appearance='ghost'][color='subtle']) { + color: var(--colorNeutralForegroundInverted); +} + +:host([appearance='ghost'][color='success']) { + color: var(--colorPaletteGreenForeground3); +} + +:host([appearance='ghost'][color='warning']) { + color: var(--colorPaletteYellowForeground2); +} + +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} +:host { + position: relative; + box-sizing: border-box; + align-items: center; + justify-content: center; + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + min-width: 20px; + height: 20px; + padding-inline: calc(var(--spacingHorizontalXS) + var(--spacingHorizontalXXS)); + border-radius: var(--borderRadiusCircular); + border-color: var(--colorTransparentStroke); + background-color: var(--colorBrandBackground); + color: var(--colorNeutralForegroundOnBrand); + contain: content; +} + +::slotted(svg) { + font-size: 12px; +} + +:host(:not([appearance='ghost']))::after { + position: absolute; + content: ''; + top: 0; + left: 0; + bottom: 0; + right: 0; + border-style: solid; + border-width: var(--strokeWidthThin); + border-color: inherit; + border-radius: inherit; +} + +:host(:is([dot], [dot][appearance][size])) { + min-width: auto; + width: 6px; + height: 6px; + padding: 0; +} diff --git a/packages/web-components/src/counter-badge/counter-badge.template.html b/packages/web-components/src/counter-badge/counter-badge.template.html new file mode 100644 index 0000000000000..c87df64f4b633 --- /dev/null +++ b/packages/web-components/src/counter-badge/counter-badge.template.html @@ -0,0 +1,8 @@ + + + diff --git a/packages/web-components/src/dialog-body/dialog-body.styles.css b/packages/web-components/src/dialog-body/dialog-body.styles.css new file mode 100644 index 0000000000000..add183a4e11d2 --- /dev/null +++ b/packages/web-components/src/dialog-body/dialog-body.styles.css @@ -0,0 +1,92 @@ +:host([hidden]) { + display: none; +} +:host { + display: grid; +} + +:host { + background: var(--colorNeutralBackground1); + box-sizing: border-box; + gap: var(--spacingVerticalS); + padding: var(--spacingVerticalXXL) var(--spacingHorizontalXXL); + container: dialog-body / inline-size; +} + +.title { + box-sizing: border-box; + align-items: flex-start; + background: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground1); + column-gap: 8px; + display: flex; + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase500); + font-weight: var(--fontWeightSemibold); + inset-block-start: 0; + justify-content: space-between; + line-height: var(--lineHeightBase500); + margin-block-end: calc(var(--spacingVerticalS) * -1); + margin-block-start: calc(var(--spacingVerticalXXL) * -1); + padding-block-end: var(--spacingVerticalS); + padding-block-start: var(--spacingVerticalXXL); +} + +.content { + box-sizing: border-box; + color: var(--colorNeutralForeground1); + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + font-weight: var(--fontWeightRegular); + line-height: var(--lineHeightBase300); + min-height: 32px; +} + +.actions { + box-sizing: border-box; + background: var(--colorNeutralBackground1); + display: flex; + flex-direction: column; + gap: var(--spacingVerticalS); + inset-block-end: 0; + margin-block-end: calc(var(--spacingVerticalXXL) * -1); + padding-block-end: var(--spacingVerticalXXL); + padding-block-start: var(--spacingVerticalL); +} + +::slotted([slot='title-action']) { + margin-inline-start: auto; +} + +::slotted([slot='title']) { + font: inherit; + padding: 0; + margin: 0; +} + +/* align title content to the end when there is no title*/ +:not(:has(:is([slot='title'], [slot='title-action']))) .title { + justify-content: end; +} + +@container (min-width: 480px) { + .actions { + align-items: center; + flex-direction: row; + justify-content: flex-end; + margin-block-start: calc(var(--spacingVerticalS) * -1); + padding-block-start: var(--spacingVerticalS); + } +} + +/* For a11y, set sticky position for title and actions when the viewport is tall enough */ +@media (min-height: 480px) { + .title { + position: sticky; + z-index: 1; + } + .actions { + position: sticky; + z-index: 2; + } +} diff --git a/packages/web-components/src/dialog-body/dialog-body.template.html b/packages/web-components/src/dialog-body/dialog-body.template.html new file mode 100644 index 0000000000000..16605d782db4d --- /dev/null +++ b/packages/web-components/src/dialog-body/dialog-body.template.html @@ -0,0 +1,12 @@ + + + diff --git a/packages/web-components/src/dialog/dialog.styles.css b/packages/web-components/src/dialog/dialog.styles.css new file mode 100644 index 0000000000000..39f6e276c1508 --- /dev/null +++ b/packages/web-components/src/dialog/dialog.styles.css @@ -0,0 +1,85 @@ +@layer base { + :host { + --dialog-backdrop: var(--colorBackgroundOverlay); + --dialog-starting-scale: 0.85; + } + + ::backdrop { + background: var(--dialog-backdrop, rgba(0, 0, 0, 0.4)); + } + + dialog { + background: var(--colorNeutralBackground1); + border-radius: var(--borderRadiusXLarge); + border: none; + box-shadow: var(--shadow64); + color: var(--colorNeutralForeground1); + max-height: 100vh; + padding: 0; + width: 100%; + max-width: 600px; + } + + :host([type='non-modal']) dialog { + inset: 0; + position: fixed; + z-index: 2; + overflow: auto; + } + + @supports (max-height: 1dvh) { + dialog { + max-height: 100dvh; + } + } +} + +@layer animations { + /* Disable animations for reduced motion */ + @media (prefers-reduced-motion: no-preference) { + dialog, + ::backdrop { + transition: display allow-discrete, opacity, overlay allow-discrete, scale; + transition-duration: var(--durationGentle); + transition-timing-function: var(--curveDecelerateMid); + /* Set opacity to 0 when closed */ + opacity: 0; + } + ::backdrop { + transition-timing-function: var(--curveLinear); + } + + /* Set opacity to 1 when open */ + [open], + [open]::backdrop { + opacity: 1; + } + + /* Exit styles for dialog */ + dialog:not([open]) { + /* Make small when leaving */ + scale: var(--dialog-starting-scale); + /* Faster leaving the stage then entering */ + transition-timing-function: var(--curveAccelerateMid); + } + } + + @starting-style { + [open], + [open]::backdrop { + opacity: 0; + } + + dialog { + scale: var(--dialog-starting-scale); + } + } +} + +@media (forced-colors: active) { + @layer base { + dialog { + border: var(--strokeWidthThin) solid var(--colorTransparentStroke); + } + } +} diff --git a/packages/web-components/src/dialog/dialog.template.html b/packages/web-components/src/dialog/dialog.template.html new file mode 100644 index 0000000000000..790033f1145f8 --- /dev/null +++ b/packages/web-components/src/dialog/dialog.template.html @@ -0,0 +1,19 @@ + + + diff --git a/packages/web-components/src/divider/divider.styles.css b/packages/web-components/src/divider/divider.styles.css new file mode 100644 index 0000000000000..5692609d5174e --- /dev/null +++ b/packages/web-components/src/divider/divider.styles.css @@ -0,0 +1,123 @@ +:host([hidden]) { + display: none; +} +:host { + display: flex; +} + +:host { + contain: content; +} + +:host::after, +:host::before { + align-self: center; + background: var(--colorNeutralStroke2); + box-sizing: border-box; + content: ''; + display: flex; + flex-grow: 1; + height: var(--strokeWidthThin); +} + +:host([inset]) { + padding: 0 12px; +} + +:host ::slotted(*) { + color: var(--colorNeutralForeground2); + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase200); + font-weight: var(--fontWeightRegular); + margin: 0; + padding: 0 12px; +} + +:host([align-content='start'])::before, +:host([align-content='end'])::after { + flex-basis: 12px; + flex-grow: 0; + flex-shrink: 0; +} + +:host([orientation='vertical']) { + align-items: center; + flex-direction: column; + height: 100%; + min-height: 84px; +} + +:host([orientation='vertical']):empty { + min-height: 20px; +} + +:host([orientation='vertical'][inset])::before { + margin-top: 12px; +} +:host([orientation='vertical'][inset])::after { + margin-bottom: 12px; +} + +:host([orientation='vertical']):empty::before, +:host([orientation='vertical']):empty::after { + height: 10px; + min-height: 10px; + flex-grow: 0; +} + +:host([orientation='vertical'])::before, +:host([orientation='vertical'])::after { + width: var(--strokeWidthThin); + min-height: 20px; + height: 100%; +} + +:host([orientation='vertical']) ::slotted(*) { + display: flex; + flex-direction: column; + padding: 12px 0; + line-height: 20px; +} + +:host([orientation='vertical'][align-content='start'])::before { + min-height: 8px; +} +:host([orientation='vertical'][align-content='end'])::after { + min-height: 8px; +} + +:host([appearance='strong'])::before, +:host([appearance='strong'])::after { + background: var(--colorNeutralStroke1); +} +:host([appearance='strong']) ::slotted(*) { + color: var(--colorNeutralForeground1); +} +:host([appearance='brand'])::before, +:host([appearance='brand'])::after { + background: var(--colorBrandStroke1); +} +:host([appearance='brand']) ::slotted(*) { + color: var(--colorBrandForeground1); +} +:host([appearance='subtle'])::before, +:host([appearance='subtle'])::after { + background: var(--colorNeutralStroke3); +} +:host([appearance='subtle']) ::slotted(*) { + color: var(--colorNeutralForeground3); +} + +@media (forced-colors: active) { + :host([appearance='strong'])::before, + :host([appearance='strong'])::after, + :host([appearance='brand'])::before, + :host([appearance='brand'])::after, + :host([appearance='subtle'])::before, + :host([appearance='subtle'])::after, + :host::after, + :host::before { + background: WindowText; + color: WindowText; + } +} diff --git a/packages/web-components/src/divider/divider.template.html b/packages/web-components/src/divider/divider.template.html new file mode 100644 index 0000000000000..5661b5eec61b9 --- /dev/null +++ b/packages/web-components/src/divider/divider.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/drawer-body/drawer-body.styles.css b/packages/web-components/src/drawer-body/drawer-body.styles.css new file mode 100644 index 0000000000000..c5596462ee355 --- /dev/null +++ b/packages/web-components/src/drawer-body/drawer-body.styles.css @@ -0,0 +1,36 @@ +:host([hidden]) { + display: none; +} +:host { + display: grid; +} +:host { + box-sizing: border-box; + grid-template-rows: min-content auto min-content; + position: relative; + height: 100%; + padding: var(--spacingHorizontalXL); + max-height: 100svh; +} +.header { + display: flex; + justify-content: space-between; + align-items: center; + + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase500); + line-height: var(--lineHeightBase500); + font-weight: var(--fontWeightSemibold); +} + +.footer { + display: flex; + justify-content: flex-start; + gap: var(--spacingHorizontalM); +} + +::slotted([slot='title']) { + font: inherit; + padding: 0; + margin: 0; +} diff --git a/packages/web-components/src/drawer-body/drawer-body.template.html b/packages/web-components/src/drawer-body/drawer-body.template.html new file mode 100644 index 0000000000000..96ca06a3fae36 --- /dev/null +++ b/packages/web-components/src/drawer-body/drawer-body.template.html @@ -0,0 +1,15 @@ + + + diff --git a/packages/web-components/src/drawer/drawer.styles.css b/packages/web-components/src/drawer/drawer.styles.css new file mode 100644 index 0000000000000..111b3b9fce863 --- /dev/null +++ b/packages/web-components/src/drawer/drawer.styles.css @@ -0,0 +1,134 @@ +:host([hidden]) { + display: none; +} +:host { + display: block; +} + +:host { + --dialog-backdrop: var(--colorBackgroundOverlay); +} + +:host([type='non-modal']) dialog[open]::backdrop { + display: none; +} + +:host([type='non-modal']) dialog { + position: fixed; + top: 0; + bottom: 0; +} + +:host([type='inline']) { + height: 100%; + width: fit-content; +} + +:host([type='inline']) dialog[open] { + box-shadow: none; + position: relative; +} + +:host([size='small']) dialog { + width: 320px; + max-width: 320px; +} + +:host([size='large']) dialog { + width: 940px; + max-width: 940px; +} + +:host([size='full']) dialog { + width: 100%; + max-width: 100%; +} + +:host([position='end']) dialog { + margin-inline-start: auto; + margin-inline-end: 0; +} + +dialog { + box-sizing: border-box; + z-index: var(--drawer-elevation, 1000); + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightRegular); + color: var(--colorNeutralForeground1); + max-width: var(--drawer-width, 592px); + max-height: 100vh; + height: 100%; + margin-inline-start: 0; + margin-inline-end: auto; + border-inline-end-color: var(--colorTransparentStroke); + border-inline-start-color: var(--drawer-separator, var(--colorTransparentStroke)); + outline: none; + top: 0; + bottom: 0; + width: var(--drawer-width, 592px); + border-radius: 0; + padding: 0; + max-width: var(--drawer-width, 592px); + box-shadow: var(--shadow64); + border: var(--strokeWidthThin) solid var(--colorTransparentStroke); + background: var(--colorNeutralBackground1); +} + +dialog::backdrop { + background: var(--dialog-backdrop); +} + +@layer animations { + /* Disable animations for reduced motion */ + @media (prefers-reduced-motion: no-preference) { + dialog { + transition: display allow-discrete, opacity, overlay allow-discrete, transform; + transition-duration: var(--durationGentle); + transition-timing-function: var(--curveDecelerateMid); + } + + /* Exit styles for dialog */ + :host dialog:not([open]) { + transform: translateX(-100%); + transition-timing-function: var(--curveAccelerateMid); + } + :host([position='end']) dialog:not([open]) { + transform: translateX(100%); + transition-timing-function: var(--curveAccelerateMid); + } + + dialog[open] { + transform: translateX(0); + } + + dialog::backdrop { + transition: display allow-discrete, opacity, overlay allow-discrete, scale; + transition-duration: var(--durationGentle); + transition-timing-function: var(--curveDecelerateMid); + background: var(--dialog-backdrop, var(--colorBackgroundOverlay)); + opacity: 0; + } + + dialog[open]::backdrop { + opacity: 1; + } + + dialog::backdrop { + transition-timing-function: var(--curveLinear); + } + } + + @starting-style { + dialog[open] { + transform: translateX(-100%); + } + :host([position='end']) dialog[open] { + transform: translateX(100%); + } + dialog[open]::backdrop { + opacity: 0; + } + } +} diff --git a/packages/web-components/src/drawer/drawer.template.html b/packages/web-components/src/drawer/drawer.template.html new file mode 100644 index 0000000000000..6b0f2ae2b54b8 --- /dev/null +++ b/packages/web-components/src/drawer/drawer.template.html @@ -0,0 +1,21 @@ + + + diff --git a/packages/web-components/src/dropdown/dropdown.styles.css b/packages/web-components/src/dropdown/dropdown.styles.css new file mode 100644 index 0000000000000..4786eacf19905 --- /dev/null +++ b/packages/web-components/src/dropdown/dropdown.styles.css @@ -0,0 +1,223 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + anchor-name: --dropdown-trigger; + box-sizing: border-box; + color: var(--colorNeutralForeground1); + cursor: pointer; +} + +:host(:state(placeholder-shown)) { + color: var(--colorNeutralForeground4); +} + +.control { + appearance: none; + background-color: var(--colorNeutralBackground1); + border-radius: var(--borderRadiusMedium); + border: var(--strokeWidthThin) solid var(--colorTransparentStroke); + box-shadow: inset 0 0 0 var(--strokeWidthThin) var(--control-border-color); + box-sizing: border-box; + color: inherit; + column-gap: var(--spacingHorizontalXXS); + display: inline-flex; + justify-content: space-between; + min-width: 160px; + overflow: hidden; + padding: var(--spacingVerticalSNudge) var(--spacingHorizontalMNudge); + white-space: normal; + position: relative; + text-align: start; + width: 100%; + z-index: 1; + + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); + font-weight: var(--fontWeightRegular); +} + +:host([size='small']) .control { + column-gap: var(--spacingHorizontalXXS); + padding: var(--spacingVerticalXS) var(--spacingHorizontalSNudge); + + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + font-weight: var(--fontWeightRegular); +} + +:host([size='large']) .control { + column-gap: var(--spacingHorizontalS); + padding: var(--spacingVerticalS) var(--spacingHorizontalM); + + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); + font-weight: var(--fontWeightRegular); +} + +::slotted(:is(input, button)) { + all: unset; + flex: 1 1 auto; +} + +::slotted(button) { + cursor: pointer; +} + +::slotted(input) { + cursor: text; +} + +:where(slot[name='indicator'] > *, ::slotted([slot='indicator'])) { + all: unset; + align-items: center; + appearance: none; + aspect-ratio: 1; + color: var(--colorNeutralForeground3); + display: inline-flex; + justify-content: center; + width: 20px; +} + +:host([size='small']) :where(slot[name='indicator'] > *, ::slotted([slot='indicator'])) { + width: 16px; +} + +:host([size='large']) :where(slot[name='indicator'] > *, ::slotted([slot='indicator'])) { + width: 24px; +} + +.control::after, +.control::before { + content: '' / ''; + inset: auto 0 0; + pointer-events: none; + position: absolute; +} + +.control::before { + height: var(--strokeWidthThin); +} + +.control::after { + background-color: var(--colorCompoundBrandStroke); + height: var(--strokeWidthThick); + scale: 0 1; + transition: scale var(--durationUltraFast) var(--curveDecelerateMid); +} + +/** + * focus-ring style uses lingering :focus-within selector due to platform limitations + * TODO: Convert selector to `:host(:has(:focus-visible)) .control` when browser support increases + * ISSUE: https://issues.chromium.org/issues/40062355 + */ +:host(:where(:focus-within)) .control { + border-radius: var(--borderRadiusMedium); + box-shadow: inset 0 0 0 1px var(--colorStrokeFocus1); + outline: var(--strokeWidthThick) solid var(--colorStrokeFocus2); +} + +:host(:where(:state(open), :focus-within)) .control::after { + scale: 1 1; + transition-duration: var(--durationNormal); + transition-timing-function: var(--curveAccelerateMid); +} + +:host(:where([appearance='outline'], [appearance='transparent'])) .control::before { + background-color: var(--colorNeutralStrokeAccessible); +} + +:host([appearance='transparent']) .control { + --control-border-color: var(--colorTransparentStrokeInteractive); + background-color: var(--colorTransparentBackground); + border-radius: var(--borderRadiusNone); +} + +:host([appearance='outline']) .control { + --control-border-color: var(--colorNeutralStroke1); +} + +:host([appearance='outline']) .control:hover { + --control-border-color: var(--colorNeutralStroke1Hover); +} + +:host(:where([appearance='outline'], [appearance='transparent'])) .control:hover::before { + background-color: var(--colorNeutralStrokeAccessibleHover); +} + +:host([appearance='outline']) .control:hover::after { + background-color: var(--colorCompoundBrandBackgroundHover); +} + +:host([appearance='outline']) .control:active { + --control-border-color: var(--colorNeutralStroke1Pressed); +} + +:host(:where([appearance='outline'], [appearance='transparent'])) .control:active::before { + background-color: var(--colorNeutralStrokeAccessiblePressed); +} + +:host(:where([appearance='outline'], [appearance='transparent'])) .control:active::after { + background-color: var(--colorCompoundBrandBackgroundPressed); +} + +:host([appearance='filled-darker']) .control { + background-color: var(--colorNeutralBackground3); +} + +:host(:where([appearance='filled-lighter'], [appearance='filled-darker'])) .control { + --control-border-color: var(--colorTransparentStroke); +} + +:host(:disabled), +:host(:disabled) ::slotted(:where(button, input)) { + cursor: not-allowed; +} + +:host(:disabled) .control::before, +:host(:disabled) .control::after { + content: none; +} + +:host(:disabled) .control:is(*, :active, :hover), +:host(:disabled) :where(slot[name='indicator'] > *, ::slotted([slot='indicator'])) { + --control-border-color: var(--colorNeutralStrokeDisabled); + background-color: var(--colorNeutralBackgroundDisabled); + color: var(--colorNeutralForegroundDisabled); +} + +::slotted(:not([slot]):not([popover])), +::slotted([popover]:not(:popover-open)) { + display: none; +} + +@supports not (anchor-name: --anchor) { + :host { + --listbox-max-height: 50vh; + --margin-offset: calc(var(--lineHeightBase300) + (var(--spacingVerticalSNudge) * 2) + var(--strokeWidthThin)); + } + + :host([size='small']) { + --margin-offset: calc(var(--lineHeightBase200) + (var(--spacingVerticalXS) * 2) + var(--strokeWidthThin)); + } + + :host([size='large']) { + --margin-offset: calc(var(--lineHeightBase400) + (var(--spacingVerticalS) * 2) + var(--strokeWidthThin)); + } +} + +@media (forced-colors: active) { + :host(:disabled) .control { + border-color: GrayText; + } + :host(:disabled) :where(slot[name='indicator'] > *, ::slotted([slot='indicator'])) { + color: GrayText; + } +} diff --git a/packages/web-components/src/dropdown/dropdown.template.html b/packages/web-components/src/dropdown/dropdown.template.html new file mode 100644 index 0000000000000..e09ea75a95f8d --- /dev/null +++ b/packages/web-components/src/dropdown/dropdown.template.html @@ -0,0 +1,28 @@ + + + diff --git a/packages/web-components/src/field/field.styles.css b/packages/web-components/src/field/field.styles.css new file mode 100644 index 0000000000000..ef680ab42aeb9 --- /dev/null +++ b/packages/web-components/src/field/field.styles.css @@ -0,0 +1,125 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-grid; +} + +:host { + color: var(--colorNeutralForeground1); + align-items: center; + gap: 0 var(--spacingHorizontalM); + justify-items: start; +} + +:has([slot='message']) { + color: var(--colorNeutralForeground1); + row-gap: var(--spacingVerticalS); +} + +:not(::slotted([slot='label'])) { + gap: 0; +} + +:host([label-position='before']) { + grid-template-areas: 'label input' 'label message'; +} + +:host([label-position='after']) { + gap: 0; + grid-template-areas: 'input label' 'message message'; + grid-template-columns: auto 1fr; +} + +:host([label-position='after']) ::slotted([slot='input']) { + margin-inline-end: var(--spacingHorizontalM); +} + +:host([label-position='above']) { + grid-template-areas: 'label' 'input' 'message'; + row-gap: var(--spacingVerticalXXS); +} + +:host([label-position='below']) { + grid-template-areas: 'input' 'label' 'message'; + justify-items: center; +} + +:host([label-position='below']) ::slotted([slot='label']) { + margin-block-start: var(--spacingVerticalM); +} + +:host(:state(required)) ::slotted([slot='label'])::after { + content: '*' / ''; + color: var(--colorPaletteRedForeground1); + margin-inline-start: var(--spacingHorizontalXS); +} + +::slotted([slot='input']) { + grid-area: input; +} + +::slotted([slot='message']) { + color: var(--colorNeutralForeground3); + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase200); + font-weight: var(--fontWeightRegular); + grid-area: message; + line-height: var(--lineHeightBase200); + margin-block-start: var(--spacingVerticalXXS); +} + +:host(:state(focus-visible):focus-within) { + border-radius: var(--borderRadiusMedium); + outline: var(--strokeWidthThick) solid var(--colorStrokeFocus2); +} + +::slotted(label), +::slotted([slot='label']) { + cursor: inherit; + display: inline-flex; + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + font-weight: var(--fontWeightRegular); + grid-area: label; + line-height: var(--lineHeightBase300); + justify-self: stretch; + user-select: none; +} + +:host([size='small']) ::slotted(label) { + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); +} + +:host([size='large']) ::slotted(label) { + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} + +:host([size='large']) ::slotted(label), +:host([weight='semibold']) ::slotted(label) { + font-weight: var(--fontWeightSemibold); +} + +:host(:state(disabled)) { + cursor: default; +} + +::slotted([flag]) { + display: none; +} + +:host(:state(bad-input)) ::slotted([flag='bad-input']), +:host(:state(custom-error)) ::slotted([flag='custom-error']), +:host(:state(pattern-mismatch)) ::slotted([flag='pattern-mismatch']), +:host(:state(range-overflow)) ::slotted([flag='range-overflow']), +:host(:state(range-underflow)) ::slotted([flag='range-underflow']), +:host(:state(step-mismatch)) ::slotted([flag='step-mismatch']), +:host(:state(too-long)) ::slotted([flag='too-long']), +:host(:state(too-short)) ::slotted([flag='too-short']), +:host(:state(type-mismatch)) ::slotted([flag='type-mismatch']), +:host(:state(value-missing)) ::slotted([flag='value-missing']), +:host(:state(valid)) ::slotted([flag='valid']) { + display: block; +} diff --git a/packages/web-components/src/field/field.template.html b/packages/web-components/src/field/field.template.html new file mode 100644 index 0000000000000..ebf8f7bdbd961 --- /dev/null +++ b/packages/web-components/src/field/field.template.html @@ -0,0 +1,13 @@ + + + diff --git a/packages/web-components/src/image/image.styles.css b/packages/web-components/src/image/image.styles.css new file mode 100644 index 0000000000000..421669e2ed2f7 --- /dev/null +++ b/packages/web-components/src/image/image.styles.css @@ -0,0 +1,50 @@ +:host { + contain: content; +} + +:host ::slotted(img) { + box-sizing: border-box; + min-height: 8px; + min-width: 8px; + display: inline-block; +} +:host([block]) ::slotted(img) { + width: 100%; + height: auto; +} +:host([bordered]) ::slotted(img) { + border: var(--strokeWidthThin) solid var(--colorNeutralStroke2); +} +:host([fit='none']) ::slotted(img) { + object-fit: none; + object-position: top left; + height: 100%; + width: 100%; +} +:host([fit='center']) ::slotted(img) { + object-fit: none; + object-position: center; + height: 100%; + width: 100%; +} +:host([fit='contain']) ::slotted(img) { + object-fit: contain; + object-position: center; + height: 100%; + width: 100%; +} +:host([fit='cover']) ::slotted(img) { + object-fit: cover; + object-position: center; + height: 100%; + width: 100%; +} +:host([shadow]) ::slotted(img) { + box-shadow: var(--shadow4); +} +:host([shape='circular']) ::slotted(img) { + border-radius: var(--borderRadiusCircular); +} +:host([shape='rounded']) ::slotted(img) { + border-radius: var(--borderRadiusMedium); +} diff --git a/packages/web-components/src/image/image.template.html b/packages/web-components/src/image/image.template.html new file mode 100644 index 0000000000000..9a0ea89ffec37 --- /dev/null +++ b/packages/web-components/src/image/image.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/label/label.styles.css b/packages/web-components/src/label/label.styles.css new file mode 100644 index 0000000000000..7d521f999ab0c --- /dev/null +++ b/packages/web-components/src/label/label.styles.css @@ -0,0 +1,40 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + color: var(--colorNeutralForeground1); + cursor: pointer; + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + font-weight: var(--fontWeightRegular); + line-height: var(--lineHeightBase300); + user-select: none; +} + +.asterisk { + color: var(--colorPaletteRedForeground1); + margin-inline-start: var(--spacingHorizontalXS); +} + +:host([size='small']) { + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); +} + +:host([size='large']) { + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} + +:host(:is([size='large'], [weight='semibold'])) { + font-weight: var(--fontWeightSemibold); +} + +:host([disabled]), +:host([disabled]) .asterisk { + color: var(--colorNeutralForegroundDisabled); +} diff --git a/packages/web-components/src/label/label.template.html b/packages/web-components/src/label/label.template.html new file mode 100644 index 0000000000000..ba1bbf7493ab3 --- /dev/null +++ b/packages/web-components/src/label/label.template.html @@ -0,0 +1,7 @@ + + + diff --git a/packages/web-components/src/link/link.styles.css b/packages/web-components/src/link/link.styles.css new file mode 100644 index 0000000000000..3270cd6b093db --- /dev/null +++ b/packages/web-components/src/link/link.styles.css @@ -0,0 +1,72 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline; +} + +:host { + position: relative; + box-sizing: border-box; + background-color: transparent; + color: var(--colorBrandForegroundLink); + cursor: pointer; + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + font-weight: var(--fontWeightRegular); + overflow: inherit; + text-align: start; + text-decoration: none; + text-decoration-thickness: var(--strokeWidthThin); + text-overflow: inherit; + user-select: text; +} + +:host(:is(:hover, :focus-visible)) { + outline: none; + text-decoration-line: underline; +} + +@media (hover: hover) { + :host(:hover) { + color: var(--colorBrandForegroundLinkHover); + } + + :host(:active) { + color: var(--colorBrandForegroundLinkPressed); + } + + :host([appearance='subtle']:hover) { + color: var(--colorNeutralForeground2LinkHover); + } + + :host([appearance='subtle']:active) { + color: var(--colorNeutralForeground2LinkPressed); + } +} + +:host([appearance='subtle']) { + color: var(--colorNeutralForeground2Link); +} + +:host-context(:is(h1, h2, h3, h4, h5, h6, p, fluent-text)), +:host([inline]) { + font: inherit; + text-decoration: underline; +} + +:host(:not([href])) { + color: inherit; + text-decoration: none; +} + +::slotted(a) { + position: absolute; + inset: 0; +} + +@media (forced-colors: active) { + :host { + color: LinkText; + } +} diff --git a/packages/web-components/src/link/link.template.html b/packages/web-components/src/link/link.template.html new file mode 100644 index 0000000000000..6014bf970c3e7 --- /dev/null +++ b/packages/web-components/src/link/link.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/listbox/listbox.styles.css b/packages/web-components/src/listbox/listbox.styles.css new file mode 100644 index 0000000000000..c9acaeb45ec7e --- /dev/null +++ b/packages/web-components/src/listbox/listbox.styles.css @@ -0,0 +1,55 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + background-color: var(--colorNeutralBackground1); + border-radius: var(--borderRadiusMedium); + border: var(--strokeWidthThin) solid var(--colorTransparentStroke); + box-shadow: var(--shadow16); + box-sizing: border-box; + flex-direction: column; + margin: 0; + min-width: 160px; + padding: var(--spacingHorizontalXS); + row-gap: var(--spacingHorizontalXXS); + width: auto; +} + +:host([popover]) { + inset: unset; + overflow: auto; +} + +@supports (anchor-name: --anchor) { + :host([popover]) { + position: absolute; + margin-block-start: 0; + max-height: var(--listbox-max-height, calc(50vh - anchor-size(self-block))); + min-width: anchor-size(width); + position-anchor: --dropdown; + position-area: block-end span-inline-end; + position-try-fallbacks: flip-inline, flip-block, --flip-block, block-start; + } + + @position-try --flip-block { + bottom: anchor(top); + top: unset; + } +} + +@supports not (anchor-name: --anchor) { + :host([popover]) { + margin-block-start: var(--margin-offset, 0); + max-height: var(--listbox-max-height, 50vh); + position: fixed; + } + + :host([popover]:state(flip-block)) { + margin-block-start: revert; + translate: 0 -100%; + } +} diff --git a/packages/web-components/src/listbox/listbox.template.html b/packages/web-components/src/listbox/listbox.template.html new file mode 100644 index 0000000000000..c4717d05fdb6e --- /dev/null +++ b/packages/web-components/src/listbox/listbox.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/menu-button/menu-button.styles.css b/packages/web-components/src/menu-button/menu-button.styles.css new file mode 100644 index 0000000000000..15bddfefdbf8a --- /dev/null +++ b/packages/web-components/src/menu-button/menu-button.styles.css @@ -0,0 +1,268 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + --icon-spacing: var(--spacingHorizontalSNudge); + position: relative; + contain: layout style; + vertical-align: middle; + align-items: center; + box-sizing: border-box; + justify-content: center; + text-align: center; + text-decoration-line: none; + margin: 0; + min-height: 32px; + outline-style: none; + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground1); + border: var(--strokeWidthThin) solid var(--colorNeutralStroke1); + padding: 0 var(--spacingHorizontalM); + min-width: 96px; + border-radius: var(--borderRadiusMedium); + font-size: var(--fontSizeBase300); + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + line-height: var(--lineHeightBase300); + transition-duration: var(--durationFaster); + transition-property: background, border, color; + transition-timing-function: var(--curveEasyEase); + cursor: pointer; + user-select: none; +} + +.content { + display: inherit; +} + +:host(:hover) { + background-color: var(--colorNeutralBackground1Hover); + color: var(--colorNeutralForeground1Hover); + border-color: var(--colorNeutralStroke1Hover); +} + +:host(:hover:active) { + background-color: var(--colorNeutralBackground1Pressed); + border-color: var(--colorNeutralStroke1Pressed); + color: var(--colorNeutralForeground1Pressed); + outline-style: none; +} + +:host(:focus-visible) { + border-color: var(--colorTransparentStroke); + outline: var(--strokeWidthThick) solid var(--colorTransparentStroke); + box-shadow: var(--shadow4), 0 0 0 2px var(--colorStrokeFocus2); +} + +@media screen and (prefers-reduced-motion: reduce) { + :host { + transition-duration: 0.01ms; + } +} + +::slotted(svg) { + font-size: 20px; + height: 20px; + width: 20px; + fill: currentColor; +} + +::slotted([slot='start']) { + margin-inline-end: var(--icon-spacing); +} + +::slotted([slot='end']), +[slot='end'] { + flex-shrink: 0; + margin-inline-start: var(--icon-spacing); +} + +:host([icon-only]) { + min-width: 32px; + max-width: 32px; +} + +:host([size='small']) { + --icon-spacing: var(--spacingHorizontalXS); + min-height: 24px; + min-width: 64px; + padding: 0 var(--spacingHorizontalS); + border-radius: var(--borderRadiusSmall); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + font-weight: var(--fontWeightRegular); +} + +:host([size='small'][icon-only]) { + min-width: 24px; + max-width: 24px; +} + +:host([size='large']) { + min-height: 40px; + border-radius: var(--borderRadiusLarge); + padding: 0 var(--spacingHorizontalL); + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} + +:host([size='large'][icon-only]) { + min-width: 40px; + max-width: 40px; +} + +:host([size='large']) ::slotted(svg) { + font-size: 24px; + height: 24px; + width: 24px; +} + +:host(:is([shape='circular'], [shape='circular']:focus-visible)) { + border-radius: var(--borderRadiusCircular); +} + +:host(:is([shape='square'], [shape='square']:focus-visible)) { + border-radius: var(--borderRadiusNone); +} + +:host([appearance='primary']) { + background-color: var(--colorBrandBackground); + color: var(--colorNeutralForegroundOnBrand); + border-color: transparent; +} + +:host([appearance='primary']:hover) { + background-color: var(--colorBrandBackgroundHover); +} + +:host([appearance='primary']:is(:hover, :hover:active):not(:focus-visible)) { + border-color: transparent; +} + +:host([appearance='primary']:is(:hover, :hover:active)) { + color: var(--colorNeutralForegroundOnBrand); +} + +:host([appearance='primary']:hover:active) { + background-color: var(--colorBrandBackgroundPressed); +} + +:host([appearance='primary']:focus-visible) { + border-color: var(--colorNeutralForegroundOnBrand); + box-shadow: var(--shadow2), 0 0 0 2px var(--colorStrokeFocus2); +} + +:host([appearance='outline']) { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='outline']:hover) { + background-color: var(--colorTransparentBackgroundHover); +} + +:host([appearance='outline']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); +} + +:host([appearance='subtle']) { + background-color: var(--colorSubtleBackground); + color: var(--colorNeutralForeground2); + border-color: transparent; +} + +:host([appearance='subtle']:hover) { + background-color: var(--colorSubtleBackgroundHover); + color: var(--colorNeutralForeground2Hover); + border-color: transparent; +} + +:host([appearance='subtle']:hover:active) { + background-color: var(--colorSubtleBackgroundPressed); + color: var(--colorNeutralForeground2Pressed); + border-color: transparent; +} + +:host([appearance='subtle']:hover) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='subtle']:hover:active) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandPressed); +} + +:host([appearance='transparent']) { + background-color: var(--colorTransparentBackground); + color: var(--colorNeutralForeground2); +} + +:host([appearance='transparent']:hover) { + background-color: var(--colorTransparentBackgroundHover); + color: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='transparent']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); + color: var(--colorNeutralForeground2BrandPressed); +} + +:host(:is([appearance='transparent'], [appearance='transparent']:is(:hover, :active))) { + border-color: transparent; +} + +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])), +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable]):hover), +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable]):hover:active) { + background-color: var(--colorNeutralBackgroundDisabled); + border-color: var(--colorNeutralStrokeDisabled); + color: var(--colorNeutralForegroundDisabled); + cursor: not-allowed; +} + +:host([appearance='primary']:is(:disabled, [disabled-focusable])), +:host([appearance='primary']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + border-color: transparent; +} + +:host([appearance='outline']:is(:disabled, [disabled-focusable])), +:host([appearance='outline']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='subtle']:is(:disabled, [disabled-focusable])), +:host([appearance='subtle']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + background-color: var(--colorTransparentBackground); + border-color: transparent; +} + +:host([appearance='transparent']:is(:disabled, [disabled-focusable])), +:host([appearance='transparent']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + border-color: transparent; + background-color: var(--colorTransparentBackground); +} + +@media (forced-colors: active) { + :host { + background-color: ButtonFace; + color: ButtonText; + } + + :host(:is(:hover, :focus-visible)) { + border-color: Highlight !important; + } + + :host([appearance='primary']:not(:is(:hover, :focus-visible))) { + background-color: Highlight; + color: HighlightText; + forced-color-adjust: none; + } + + :host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])) { + background-color: ButtonFace; + color: GrayText; + border-color: ButtonText; + } +} diff --git a/packages/web-components/src/menu-button/menu-button.template.html b/packages/web-components/src/menu-button/menu-button.template.html new file mode 100644 index 0000000000000..18c8a1e2b13e8 --- /dev/null +++ b/packages/web-components/src/menu-button/menu-button.template.html @@ -0,0 +1,25 @@ + + + diff --git a/packages/web-components/src/menu-item/menu-item.styles.css b/packages/web-components/src/menu-item/menu-item.styles.css new file mode 100644 index 0000000000000..b0935e3646bf3 --- /dev/null +++ b/packages/web-components/src/menu-item/menu-item.styles.css @@ -0,0 +1,156 @@ +:host([hidden]) { + display: none; +} +:host { + display: grid; +} + +:host { + --indent: 0; + align-items: center; + background: var(--colorNeutralBackground1); + border-radius: var(--borderRadiusMedium); + color: var(--colorNeutralForeground2); + contain: layout; + cursor: pointer; + /* Prevent shrinking of MenuItems when max-height is applied to MenuList */ + flex-shrink: 0; + font: var(--fontWeightRegular) var(--fontSizeBase300) / var(--lineHeightBase300) var(--fontFamilyBase); + grid-gap: 4px; + grid-template-columns: 20px 20px auto 20px; + height: 32px; + overflow: visible; + padding: 0 10px; +} + +:host(:hover) { + background: var(--colorNeutralBackground1Hover); + color: var(--colorNeutralForeground2Hover); +} + +:host(:active) { + background-color: var(--colorNeutralBackground1Selected); + color: var(--colorNeutralForeground2Pressed); +} + +:host(:active) ::slotted([slot='start']) { + color: var(--colorCompoundBrandForeground1Pressed); +} + +:host(:state(disabled)) { + background-color: var(--colorNeutralBackgroundDisabled); + color: var(--colorNeutralForegroundDisabled); +} + +:host(:state(disabled)) ::slotted([slot='start']), +:host(:state(disabled)) ::slotted([slot='end']) { + color: var(--colorNeutralForegroundDisabled); +} + +:host(:focus-visible) { + border-radius: var(--borderRadiusMedium); + outline: 2px solid var(--colorStrokeFocus2); +} + +.content { + white-space: nowrap; + flex-grow: 1; + grid-column: auto / span 2; + padding: 0 2px; +} + +:host(:not(:state(checked))) .indicator, +:host(:not(:state(checked))) ::slotted([slot='indicator']), +:host(:not(:state(submenu))) .submenu-glyph, +:host(:not(:state(submenu))) ::slotted([slot='submenu-glyph']) { + display: none; +} + +::slotted([slot='end']) { + color: var(--colorNeutralForeground3); + font: var(--fontWeightRegular) var(--fontSizeBase200) / var(--lineHeightBase200) var(--fontFamilyBase); + white-space: nowrap; +} + +:host([data-indent='1']) { + --indent: 1; +} + +:host([data-indent='2']) { + --indent: 2; + grid-template-columns: 20px 20px auto auto; +} + +:host(:state(submenu)) { + grid-template-columns: 20px auto auto 20px; +} + +:host([data-indent='2']:state(submenu)) { + grid-template-columns: 20px 20px auto auto 20px; +} + +.indicator, +::slotted([slot='indicator']) { + grid-column: 1 / span 1; + width: 20px; +} + +::slotted([slot='start']) { + display: inline-flex; + grid-column: calc(var(--indent)) / span 1; +} + +.content { + grid-column: calc(var(--indent) + 1) / span 1; +} + +::slotted([slot='end']) { + grid-column: calc(var(--indent) + 2) / span 1; + justify-self: end; +} + +.submenu-glyph, +::slotted([slot='submenu-glyph']) { + grid-column: -2 / span 1; + justify-self: end; +} + +@layer popover { + :host { + anchor-name: --menu-trigger; + position: relative; + } + + ::slotted([popover]) { + margin: 0; + max-height: var(--menu-max-height, auto); + position: absolute; + position-anchor: --menu-trigger; + position-area: inline-end span-block-end; + position-try-fallbacks: flip-inline, block-start, block-end; + z-index: 1; + } + + ::slotted([popover]:not(:popover-open)) { + display: none; + } + + ::slotted([popover]:popover-open) { + inset: unset; + } + + /* Fallback for no anchor-positioning */ + @supports not (anchor-name: --menu-trigger) { + ::slotted([popover]) { + align-self: start; + } + } +} + +@media (forced-colors: active) { + :host(:state(disabled)), + :host(:state(disabled)) ::slotted([slot='start']), + :host(:state(disabled)) ::slotted([slot='end']) { + color: GrayText; + } +} diff --git a/packages/web-components/src/menu-item/menu-item.template.html b/packages/web-components/src/menu-item/menu-item.template.html new file mode 100644 index 0000000000000..668491e6f3c70 --- /dev/null +++ b/packages/web-components/src/menu-item/menu-item.template.html @@ -0,0 +1,50 @@ + + + diff --git a/packages/web-components/src/menu-list/menu-list.styles.css b/packages/web-components/src/menu-list/menu-list.styles.css new file mode 100644 index 0000000000000..f3fe401c6df15 --- /dev/null +++ b/packages/web-components/src/menu-list/menu-list.styles.css @@ -0,0 +1,20 @@ +:host([hidden]) { + display: none; +} +:host { + display: flex; +} + +:host { + flex-direction: column; + height: fit-content; + max-width: 300px; + min-width: 160px; + width: auto; + background-color: var(--colorNeutralBackground1); + border: 1px solid var(--colorTransparentStroke); + border-radius: var(--borderRadiusMedium); + box-shadow: var(--shadow16); + padding: 4px; + row-gap: 2px; +} diff --git a/packages/web-components/src/menu-list/menu-list.template.html b/packages/web-components/src/menu-list/menu-list.template.html new file mode 100644 index 0000000000000..8438a79c6cc9f --- /dev/null +++ b/packages/web-components/src/menu-list/menu-list.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/menu/menu.styles.css b/packages/web-components/src/menu/menu.styles.css new file mode 100644 index 0000000000000..6b45d94042a02 --- /dev/null +++ b/packages/web-components/src/menu/menu.styles.css @@ -0,0 +1,57 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-block; +} + +::slotted([slot='trigger']) { + anchor-name: --menu-trigger; +} + +::slotted([popover]) { + margin: 0; + max-height: var(--menu-max-height, auto); + position-anchor: --menu-trigger; + position-area: block-end span-inline-end; + position-try-fallbacks: flip-block; + position: absolute; + z-index: 1; +} + +:host([split]) ::slotted([popover]) { + position-area: block-end span-inline-start; +} + +::slotted([popover]:popover-open) { + inset: unset; +} + +::slotted([popover]:not(:popover-open)) { + display: none; +} + +:host([split]) { + display: inline-flex; +} + +:host([split]) ::slotted([slot='primary-action']) { + border-inline-end: var(--strokeWidthThin) solid var(--colorNeutralStroke1); + border-start-end-radius: 0; + border-end-end-radius: 0; +} + +/* Keeps focus visible visuals above trigger slot*/ +:host([split]) ::slotted([slot='primary-action']:focus-visible) { + z-index: 1; +} + +:host([split]) ::slotted([slot='primary-action'][appearance='primary']) { + border-inline-end: var(--strokeWidthThin) solid white; +} + +:host([split]) ::slotted([slot='trigger']) { + border-inline-start: 0; + border-start-start-radius: 0; + border-end-start-radius: 0; +} diff --git a/packages/web-components/src/menu/menu.template.html b/packages/web-components/src/menu/menu.template.html new file mode 100644 index 0000000000000..a161e2a932ccb --- /dev/null +++ b/packages/web-components/src/menu/menu.template.html @@ -0,0 +1,8 @@ + + + diff --git a/packages/web-components/src/message-bar/message-bar.styles.css b/packages/web-components/src/message-bar/message-bar.styles.css new file mode 100644 index 0000000000000..0396ff8015419 --- /dev/null +++ b/packages/web-components/src/message-bar/message-bar.styles.css @@ -0,0 +1,97 @@ +:host { + display: grid; + box-sizing: border-box; + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + width: 100%; + background: var(--colorNeutralBackground3); + border: 1px solid var(--colorNeutralStroke1); + padding-inline: var(--spacingHorizontalM); + border-radius: var(--borderRadiusMedium); + min-height: 36px; + align-items: center; + grid-template: 'icon body actions dismiss' / auto 1fr auto auto; + contain: layout style paint; +} + +:host([shape='square']) { + border-radius: 0; +} + +:host([intent='success']) { + background-color: var(--colorPaletteGreenBackground1); + border-color: var(--colorPaletteGreenBorder1); +} + +:host([intent='warning']) { + background-color: var(--colorPaletteDarkOrangeBackground1); + border-color: var(--colorPaletteDarkOrangeBorder1); +} + +:host([intent='error']) { + background-color: var(--colorPaletteRedBackground1); + border-color: var(--colorPaletteRedBorder1); +} + +:host([layout='multiline']) { + grid-template-areas: + 'icon body dismiss' + 'actions actions actions'; + grid-template-columns: auto 1fr auto; + grid-template-rows: auto auto 1fr; + padding-block: var(--spacingVerticalMNudge); + padding-inline: var(--spacingHorizontalM); +} + +.content { + grid-area: body; + max-width: 520px; + padding-block: var(--spacingVerticalMNudge); + padding-inline: 0; +} + +:host([layout='multiline']) .content { + padding: 0; +} + +::slotted([slot='icon']) { + display: flex; + grid-area: icon; + flex-direction: column; + align-items: center; + color: var(--colorNeutralForeground3); + margin-inline-end: var(--spacingHorizontalS); +} + +:host([layout='multiline']) ::slotted([slot='icon']) { + align-items: start; + height: 100%; +} + +::slotted([slot='dismiss']) { + grid-area: dismiss; +} + +.actions { + grid-area: actions; + display: flex; + justify-self: end; + margin-inline-end: var(--spacingHorizontalS); + gap: var(--spacingHorizontalS); +} + +:host([layout='multiline']) .actions { + margin-block-start: var(--spacingVerticalMNudge); + margin-inline-end: 0; +} + +:host([layout='multiline']) ::slotted([slot='dismiss']) { + align-items: start; + height: 100%; + padding-block-start: var(--spacingVerticalS); +} + +::slotted(*) { + font-size: inherit; +} diff --git a/packages/web-components/src/message-bar/message-bar.template.html b/packages/web-components/src/message-bar/message-bar.template.html new file mode 100644 index 0000000000000..ad121b73950a9 --- /dev/null +++ b/packages/web-components/src/message-bar/message-bar.template.html @@ -0,0 +1,13 @@ + + + diff --git a/packages/web-components/src/option/option.styles.css b/packages/web-components/src/option/option.styles.css new file mode 100644 index 0000000000000..cedee7d592ca3 --- /dev/null +++ b/packages/web-components/src/option/option.styles.css @@ -0,0 +1,134 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-grid; +} + +:host { + -webkit-tap-highlight-color: transparent; + + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); + font-weight: var(--fontWeightRegular); + + align-items: center; + background-color: var(--colorNeutralBackground1); + border-radius: var(--borderRadiusMedium); + box-sizing: border-box; + color: var(--colorNeutralForeground2); + column-gap: var(--spacingHorizontalXS); + cursor: pointer; + grid-template-areas: 'indicator start content'; + grid-template-columns: auto auto 1fr; + min-height: 32px; + padding: var(--spacingHorizontalSNudge); + text-align: start; +} + +.content { + grid-area: content; + line-height: 1; +} + +::slotted([slot='start']) { + grid-area: start; +} + +:host(:hover) { + background-color: var(--colorNeutralBackground1Hover); + color: var(--colorNeutralForeground2Hover); +} + +:host(:active) { + background-color: var(--colorNeutralBackground1Pressed); + color: var(--colorNeutralForeground2Pressed); +} + +:host(:disabled) { + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForegroundDisabled); + cursor: default; +} + +.checkmark-16-filled { + fill: currentColor; + width: 16px; +} + +slot[name='checked-indicator'] > *, +::slotted([slot='checked-indicator']) { + aspect-ratio: 1; + flex: 0 0 auto; + grid-area: indicator; + visibility: hidden; +} + +:host(:state(selected)) :is(slot[name='checked-indicator'] > *, ::slotted([slot='checked-indicator'])) { + visibility: visible; +} + +:host(:state(multiple)) .checkmark-16-filled, +:host(:not(:state(multiple))) .checkmark-12-regular { + display: none; +} + +:host(:state(multiple)) .checkmark-12-regular { + background-color: var(--colorNeutralBackground1); + border-radius: var(--borderRadiusSmall); + border: var(--strokeWidthThin) solid var(--colorNeutralStrokeAccessible); + box-sizing: border-box; + cursor: pointer; + fill: transparent; + position: relative; + visibility: visible; + width: 16px; +} + +:host(:state(multiple):state(selected)) .checkmark-12-regular { + background-color: var(--colorCompoundBrandBackground); + border-color: var(--colorCompoundBrandStroke); + fill: var(--colorNeutralForegroundInverted); +} + +:host(:disabled:state(multiple)) .checkmark-12-regular { + border-color: var(--colorNeutralStrokeDisabled); +} + +:host(:disabled:state(multiple):state(selected)) .checkmark-12-regular { + background-color: var(--colorNeutralBackgroundDisabled); +} + +:host(:state(active)) { + border: var(--strokeWidthThick) solid var(--colorStrokeFocus2); +} + +@supports (selector(:host(:has(*)))) { + :host(:has([slot='start']:not([size='16']))) { + column-gap: var(--spacingHorizontalSNudge); + } +} + +:host(:state(description)) { + column-gap: var(--spacingHorizontalSNudge); + grid-template-areas: + 'indicator start content' + 'indicator start description'; +} + +::slotted([slot='description']) { + color: var(--colorNeutralForeground3); + grid-area: description; + + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + font-weight: var(--fontWeightRegular); +} + +@media (forced-colors: active) { + :host(:disabled) { + color: GrayText; + } +} diff --git a/packages/web-components/src/option/option.template.html b/packages/web-components/src/option/option.template.html new file mode 100644 index 0000000000000..e35a86ae20b6f --- /dev/null +++ b/packages/web-components/src/option/option.template.html @@ -0,0 +1,24 @@ + + + diff --git a/packages/web-components/src/progress-bar/progress-bar.styles.css b/packages/web-components/src/progress-bar/progress-bar.styles.css new file mode 100644 index 0000000000000..e8dd83b7e5e88 --- /dev/null +++ b/packages/web-components/src/progress-bar/progress-bar.styles.css @@ -0,0 +1,103 @@ +:host([hidden]) { + display: none; +} +:host { + display: block; +} + +:host { + width: 100%; + height: 2px; + overflow-x: hidden; + background-color: var(--colorNeutralBackground6); + border-radius: var(--borderRadiusMedium); + contain: content; + + @supports (width: attr(value type())) { + --max: attr(max type(), 100); + --min: attr(min type(), 0); + --value: attr(value type(), 0); + --indicator-width: clamp(0%, calc((var(--value) - var(--min)) / (var(--max) - var(--min)) * 100%), 100%); + } +} + +:host([thickness='large']) { + height: 4px; +} + +:host([shape='square']) { + border-radius: var(--borderRadiusNone); +} + +.indicator { + background-color: var(--colorCompoundBrandBackground); + border-radius: inherit; + height: 100%; +} + +:host([value]) .indicator { + transition: all 0.2s ease-in-out; + + @supports (width: attr(value type())) { + width: var(--indicator-width); + } +} + +:host(:not([value])) .indicator { + position: relative; + width: 33%; + background-image: linear-gradient( + to right, + var(--colorNeutralBackground6) 0%, + var(--colorTransparentBackground) 50%, + var(--colorNeutralBackground6) 100% + ); + animation-name: indeterminate; + animation-duration: 3s; + animation-timing-function: linear; + animation-iteration-count: infinite; +} + +:host([validation-state='error']) .indicator { + background-color: var(--colorPaletteRedBackground3); +} + +:host([validation-state='warning']) .indicator { + background-color: var(--colorPaletteDarkOrangeBackground3); +} + +:host([validation-state='success']) .indicator { + background-color: var(--colorPaletteGreenBackground3); +} + +@layer animations { + /* Disable animations for reduced motion */ + @media (prefers-reduced-motion: no-preference) { + :host([value]) { + transition: none; + } + :host(:not([value])) .indicator { + animation-duration: 0.01ms; + animation-iteration-count: 1; + } + } +} + +@keyframes indeterminate { + 0% { + inset-inline-start: -33%; + } + 100% { + inset-inline-start: 100%; + } +} + +@media (forced-colors: active) { + :host { + background-color: CanvasText; + } + .indicator, + :host(:is([validation-state='success'], [validation-state='warning'], [validation-state='error'])) .indicator { + background-color: Highlight; + } +} diff --git a/packages/web-components/src/progress-bar/progress-bar.template.html b/packages/web-components/src/progress-bar/progress-bar.template.html new file mode 100644 index 0000000000000..7ae32b218b085 --- /dev/null +++ b/packages/web-components/src/progress-bar/progress-bar.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/radio-group/radio-group.styles.css b/packages/web-components/src/radio-group/radio-group.styles.css new file mode 100644 index 0000000000000..fbf9149f2f7f5 --- /dev/null +++ b/packages/web-components/src/radio-group/radio-group.styles.css @@ -0,0 +1,45 @@ +:host([hidden]) { + display: none; +} +:host { + display: flex; +} + +:host { + -webkit-tap-highlight-color: transparent; + cursor: pointer; + gap: var(--spacingVerticalL); +} + +:host([orientation='vertical']) { + flex-direction: column; + justify-content: flex-start; +} + +:host([orientation='horizontal']) { + flex-direction: row; +} + +::slotted(*) { + color: var(--colorNeutralForeground3); +} + +::slotted(:hover) { + color: var(--colorNeutralForeground2); +} + +::slotted(:active) { + color: var(--colorNeutralForeground1); +} + +::slotted(:state(disabled)) { + color: var(--colorNeutralForegroundDisabled); +} + +::slotted(:state(checked)) { + color: var(--colorNeutralForeground1); +} + +:host([slot='input']) { + margin: var(--spacingVerticalS) var(--spacingHorizontalS); +} diff --git a/packages/web-components/src/radio-group/radio-group.template.html b/packages/web-components/src/radio-group/radio-group.template.html new file mode 100644 index 0000000000000..12026df49510b --- /dev/null +++ b/packages/web-components/src/radio-group/radio-group.template.html @@ -0,0 +1,13 @@ + + + diff --git a/packages/web-components/src/radio/radio.styles.css b/packages/web-components/src/radio/radio.styles.css new file mode 100644 index 0000000000000..44c8056cf5de4 --- /dev/null +++ b/packages/web-components/src/radio/radio.styles.css @@ -0,0 +1,119 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + --size: 16px; + aspect-ratio: 1; + background-color: var(--colorNeutralBackground1); + border: var(--strokeWidthThin) solid var(--colorNeutralStrokeAccessible); + border-radius: var(--borderRadiusCircular); + box-sizing: border-box; + position: relative; + width: var(--size); +} + +:host([size='large']) { + --size: 20px; +} + +.checked-indicator { + aspect-ratio: 1; + border-radius: var(--borderRadiusCircular); + color: var(--colorNeutralForegroundInverted); + inset: 0; + margin: auto; + position: absolute; + width: calc(var(--size) * 0.625); +} + +:host(:not([slot='input']))::after { + content: '' / ''; + position: absolute; + display: block; + inset: -8px; + box-sizing: border-box; + outline: none; + border: var(--strokeWidthThick) solid var(--colorTransparentStroke); + border-radius: var(--borderRadiusMedium); +} + +:host(:not([slot='input']):focus-visible)::after { + border-color: var(--colorStrokeFocus2); +} + +:host(:hover) { + border-color: var(--colorNeutralStrokeAccessibleHover); +} + +:host(:state(checked)) { + border-color: var(--colorCompoundBrandStroke); +} + +:host(:state(checked)) .checked-indicator { + background-color: var(--colorCompoundBrandBackground); +} + +:host(:state(checked):hover) .checked-indicator { + background-color: var(--colorCompoundBrandBackgroundHover); +} + +:host(:active) { + border-color: var(--colorNeutralStrokeAccessiblePressed); +} + +:host(:state(checked):active) .checked-indicator { + background-color: var(--colorCompoundBrandBackgroundPressed); +} + +:host(:focus-visible) { + outline: none; +} + +:host(:state(disabled)) { + background-color: var(--colorNeutralBackgroundDisabled); + border-color: var(--colorNeutralStrokeDisabled); +} + +:host(:state(checked):state(disabled)) .checked-indicator { + background-color: var(--colorNeutralStrokeDisabled); +} + +@media (forced-colors: active) { + :host { + border-color: FieldText; + } + + :host(:not([slot='input']:focus-visible))::after { + border-color: Canvas; + } + + :host(:not(:state(disabled)):hover), + :host(:not([slot='input']):focus-visible)::after { + border-color: Highlight; + } + + .checked-indicator { + color: HighlightText; + } + + :host(:state(checked)) .checked-indicator { + background-color: FieldText; + } + + :host(:state(checked):not(:state(disabled)):hover) .checked-indicator { + background-color: Highlight; + } + + :host(:state(disabled)) { + border-color: GrayText; + color: GrayText; + } + + :host(:state(disabled):state(checked)) .checked-indicator { + background-color: GrayText; + } +} diff --git a/packages/web-components/src/radio/radio.template.html b/packages/web-components/src/radio/radio.template.html new file mode 100644 index 0000000000000..264fc63357fc6 --- /dev/null +++ b/packages/web-components/src/radio/radio.template.html @@ -0,0 +1,8 @@ + + + diff --git a/packages/web-components/src/rating-display/rating-display.styles.css b/packages/web-components/src/rating-display/rating-display.styles.css new file mode 100644 index 0000000000000..38ce65999637f --- /dev/null +++ b/packages/web-components/src/rating-display/rating-display.styles.css @@ -0,0 +1,142 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + --_icon-size: 16px; + --_icon-gradient-degree: 90deg; + --_icon-color-value: var(--colorPaletteMarigoldBorderActive); + --_icon-color-empty: var(--colorPaletteMarigoldBackground2); + --_default-value: 0; + --_default-max: 5; + --_mask-image-filled: url(data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2012%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5.28347%201.54605C5.57692%200.951448%206.42479%200.951449%206.71825%201.54605L7.82997%203.79866L10.3159%204.15988C10.9721%204.25523%2011.2341%205.0616%2010.7592%205.52443L8.96043%207.27785L9.38507%209.7537C9.49716%2010.4072%208.81122%2010.9056%208.22431%2010.597L6.00086%209.4281L3.7774%2010.597C3.19049%2010.9056%202.50455%2010.4072%202.61664%209.7537L3.04128%207.27784L1.24246%205.52443C0.767651%205.0616%201.02966%204.25523%201.68584%204.15988L4.17174%203.79865L5.28347%201.54605Z%22%20%2F%3E%3C%2Fsvg%3E); + --_mask-image-outlined: url(data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2012%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%3E%3Cpath%20d%3D%22M5.28347%201.54605C5.57692%200.951448%206.42479%200.951449%206.71825%201.54605L7.82997%203.79866L10.3159%204.15988C10.9721%204.25523%2011.2341%205.0616%2010.7592%205.52443L8.96043%207.27785L9.38507%209.7537C9.49716%2010.4072%208.81122%2010.9056%208.22431%2010.597L6.00086%209.4281L3.7774%2010.597C3.19049%2010.9056%202.50455%2010.4072%202.61664%209.7537L3.04128%207.27784L1.24246%205.52443C0.767651%205.0616%201.02966%204.25523%201.68584%204.15988L4.17174%203.79865L5.28347%201.54605Z%22%20%2F%3E%3C%2Fsvg%3E); + --_mask-position-x: left; + + align-items: center; + color: var(--colorNeutralForeground1); + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + contain: layout style; + user-select: none; +} + +:host(:dir(rtl)) { + --_icon-gradient-degree: -90deg; + --_mask-position-x: right; +} + +:host([size='small']) { + --_icon-size: 12px; +} + +:host([size='large']) { + --_icon-size: 20px; + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); +} + +::slotted([slot='icon']) { + display: none; +} + +:host([color='neutral']) { + --_icon-color-value: var(--colorNeutralForeground1); + --_icon-color-empty: var(--colorNeutralBackground6); +} + +:host([color='brand']) { + --_icon-color-value: var(--colorBrandForeground1); + --_icon-color-empty: var(--colorBrandBackground2); +} + +@supports (width: attr(value type())) { + :host { + --_attr-value: attr(value type()); + --_attr-max: attr(max type()); + } +} + +:host([compact]) .display { + --_max: 1; +} + +.display { + --_value: max(0, round(var(--_attr-value, var(--_default-value)) * 2) / 2); + --_max: max(1, var(--_attr-max, var(--_default-max))); + --_mask-inline-size: calc(var(--_icon-size) + var(--spacingHorizontalXXS)); + --_icon-gradient-stop-visual-adjustment: 0px; + --_icon-gradient-stop: calc(var(--_mask-inline-size) * var(--_value) - var(--_icon-gradient-stop-visual-adjustment)); + + background-image: linear-gradient( + var(--_icon-gradient-degree), + var(--_icon-color-value) var(--_icon-gradient-stop), + var(--_icon-color-empty) calc(var(--_icon-gradient-stop) + 0.5px) + ); + block-size: var(--_icon-size); + display: grid; + inline-size: calc(var(--_max) * var(--_mask-inline-size) - var(--spacingHorizontalXXS) / 2); + mask-image: var(--_mask-image-filled); + mask-repeat: repeat no-repeat; + mask-size: var(--_mask-inline-size) var(--_icon-size); + mask-position: var(--_mask-position-x) center; +} + +.value-label, +::slotted([slot='value']) { + display: block; + margin-inline-start: var(--spacingHorizontalXS); + font-weight: var(--fontWeightSemibold); +} + +:host([size='small']) .value-label, +:host([size='small']) ::slotted([slot='value']) { + margin-inline-start: var(--spacingHorizontalXXS); +} + +:host([size='large']) .value-label, +:host([size='large']) ::slotted([slot='value']) { + margin-inline-start: var(--spacingHorizontalSNudge); +} + +:host(:not([count])) .count-label { + display: none; +} + +.count-label::before, +::slotted([slot='count'])::before { + content: '·'; + margin-inline: var(--spacingHorizontalXS); +} + +:host([size='small']) .count-label::before, +:host([size='small']) ::slotted([slot='count'])::before { + margin-inline: var(--spacingHorizontalXXS); +} + +:host([size='large']) .count-label::before, +:host([size='large']) ::slotted([slot='count'])::before { + margin-inline: var(--spacingHorizontalSNudge); +} + +@media (forced-colors: active) { + .display { + --_icon-color-value: CanvasText; + --_icon-color-empty: Canvas; + --_icon-gradient-stop-visual-adjustment: 0.5px; + + forced-color-adjust: none; + } + + .display::before { + background-color: var(--_icon-color-value); + content: ''; + grid-area: 1 / 1 / -1 / -1; + mask: inherit; + mask-image: var(--_mask-image-outlined); + } +} diff --git a/packages/web-components/src/rating-display/rating-display.template.html b/packages/web-components/src/rating-display/rating-display.template.html new file mode 100644 index 0000000000000..9d90f91bb4402 --- /dev/null +++ b/packages/web-components/src/rating-display/rating-display.template.html @@ -0,0 +1,9 @@ + + + diff --git a/packages/web-components/src/slider/slider.styles.css b/packages/web-components/src/slider/slider.styles.css new file mode 100644 index 0000000000000..2bbf60a4e9abf --- /dev/null +++ b/packages/web-components/src/slider/slider.styles.css @@ -0,0 +1,193 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-grid; +} + +:host { + --thumb-size: 20px; + --track-margin-inline: calc(var(--thumb-size) / 2); + --track-size: 4px; + --track-overhang: calc(var(--track-size) / -2); + --rail-color: var(--colorCompoundBrandBackground); + --track-color: var(--colorNeutralStrokeAccessible); + --slider-direction: 90deg; + --border-radius: var(--borderRadiusMedium); + --step-marker-inset: var(--track-overhang) -1px; + + position: relative; + align-items: center; + justify-content: center; + box-sizing: border-box; + outline: none; + user-select: none; + touch-action: none; + min-width: 120px; + min-height: 32px; + grid-template-rows: 1fr var(--thumb-size) 1fr; + grid-template-columns: var(--track-margin-inline) 1fr var(--track-margin-inline); +} + +:host(:hover) { + --rail-color: var(--colorCompoundBrandBackgroundHover); +} + +:host(:active) { + --rail-color: var(--colorCompoundBrandBackgroundPressed); +} + +:host(:disabled) { + --rail-color: var(--colorNeutralForegroundDisabled); + --track-color: var(--colorNeutralBackgroundDisabled); +} + +:host(:not(:disabled)) { + cursor: pointer; +} + +:host(:dir(rtl)) { + --slider-direction: -90deg; +} + +:host([size='small']) { + --thumb-size: 16px; + --track-overhang: -1px; + --track-size: 2px; + --border-radius: var(--borderRadiusSmall); +} + +:host([orientation='vertical']) { + --slider-direction: 0deg; + --step-marker-inset: -1px var(--track-overhang); + min-height: 120px; + grid-template-rows: var(--track-margin-inline) 1fr var(--track-margin-inline); + grid-template-columns: 1fr var(--thumb-size) 1fr; + width: unset; + min-width: 32px; + justify-items: center; +} + +:host(:not([slot='input']):focus-visible) { + box-shadow: 0 0 0 2pt var(--colorStrokeFocus2); + outline: 1px solid var(--colorStrokeFocus1); +} + +:host:after, +.track { + height: var(--track-size); + width: 100%; +} + +:host:after { + background-image: linear-gradient( + var(--slider-direction), + var(--rail-color) 0%, + var(--rail-color) 50%, + var(--track-color) 50.1%, + var(--track-color) 100% + ); + border-radius: var(--border-radius); + content: ''; + grid-row: 1 / -1; + grid-column: 1 / -1; +} + +.track { + position: relative; + background-color: var(--track-color); + grid-row: 2 / 2; + grid-column: 2 / 2; + forced-color-adjust: none; + overflow: hidden; +} + +:host([orientation='vertical'])::after, +:host([orientation='vertical']) .track { + height: 100%; + width: var(--track-size); +} + +.track::before { + content: ''; + position: absolute; + height: 100%; + border-radius: inherit; + inset-inline-start: 0; + width: var(--slider-progress); +} + +:host(:dir(rtl)) .track::before { + width: calc(100% - var(--slider-progress)); +} + +:host([orientation='vertical']) .track::before { + width: 100%; + bottom: 0; + height: var(--slider-progress); +} + +:host([step]) .track::after { + content: ''; + position: absolute; + border-radius: inherit; + inset: var(--step-marker-inset); + background-image: repeating-linear-gradient( + var(--slider-direction), + #0000 0%, + #0000 calc(var(--step-rate) - 1px), + var(--colorNeutralBackground1) calc(var(--step-rate) - 1px), + var(--colorNeutralBackground1) var(--step-rate) + ); +} + +.thumb-container { + position: absolute; + grid-row: 2 / 2; + grid-column: 2 / 2; + transform: translateX(-50%); + left: var(--slider-thumb); +} + +:host([orientation='vertical']) .thumb-container { + transform: translateY(50%); + left: unset; + bottom: var(--slider-thumb); +} + +:host(:not(:active)) :is(.thumb-container, .track::before) { + transition: all 0.2s ease; +} + +.thumb { + width: var(--thumb-size); + height: var(--thumb-size); + border-radius: var(--borderRadiusCircular); + box-shadow: 0 0 0 calc(var(--thumb-size) * 0.2) var(--colorNeutralBackground1) inset; + border: calc(var(--thumb-size) * 0.05) solid var(--colorNeutralStroke1); + box-sizing: border-box; +} + +.thumb, +.track::before { + background-color: var(--rail-color); +} + +@media (forced-colors: active) { + .track:hover, + .track:active, + .track { + background: WindowText; + } + .thumb:hover, + .thumb:active, + .thumb { + background: ButtonText; + } + + :host(:hover) .track::before, + :host(:active) .track::before, + .track::before { + background: Highlight; + } +} diff --git a/packages/web-components/src/slider/slider.template.html b/packages/web-components/src/slider/slider.template.html new file mode 100644 index 0000000000000..c22e67a795ab9 --- /dev/null +++ b/packages/web-components/src/slider/slider.template.html @@ -0,0 +1,15 @@ + + + diff --git a/packages/web-components/src/spinner/spinner.styles.css b/packages/web-components/src/spinner/spinner.styles.css new file mode 100644 index 0000000000000..c6917a8135762 --- /dev/null +++ b/packages/web-components/src/spinner/spinner.styles.css @@ -0,0 +1,159 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + --duration: 1.5s; + --indicatorSize: var(--strokeWidthThicker); + --size: 32px; + height: var(--size); + width: var(--size); + contain: strict; + content-visibility: auto; +} + +:host([size='tiny']) { + --indicatorSize: var(--strokeWidthThick); + --size: 20px; +} +:host([size='extra-small']) { + --indicatorSize: var(--strokeWidthThick); + --size: 24px; +} +:host([size='small']) { + --indicatorSize: var(--strokeWidthThick); + --size: 28px; +} +:host([size='large']) { + --indicatorSize: var(--strokeWidthThicker); + --size: 36px; +} +:host([size='extra-large']) { + --indicatorSize: var(--strokeWidthThicker); + --size: 40px; +} +:host([size='huge']) { + --indicatorSize: var(--strokeWidthThickest); + --size: 44px; +} + +.progress, +.background, +.spinner, +.start, +.end, +.indicator { + position: absolute; + inset: 0; +} + +.progress, +.spinner, +.indicator { + animation: none var(--duration) infinite var(--curveEasyEase); +} + +.progress { + animation-timing-function: linear; + animation-name: spin-linear; +} + +.background { + border: var(--indicatorSize) solid var(--colorBrandStroke2); + border-radius: 50%; +} + +:host([appearance='inverted']) .background { + border-color: rgba(255, 255, 255, 0.2); +} + +.spinner { + animation-name: spin-swing; +} + +.start { + overflow: hidden; + right: 50%; +} + +.end { + overflow: hidden; + left: 50%; +} + +.indicator { + color: var(--colorBrandStroke1); + box-sizing: border-box; + border-radius: 50%; + border: var(--indicatorSize) solid transparent; + border-block-start-color: currentcolor; + border-right-color: currentcolor; +} + +:host([appearance='inverted']) .indicator { + color: var(--colorNeutralStrokeOnBrand2); +} + +.start .indicator { + rotate: 135deg; /* Starts 9 o'clock */ + inset: 0 -100% 0 0; + animation-name: spin-start; +} + +.end .indicator { + rotate: 135deg; /* Ends at 3 o'clock */ + inset: 0 0 0 -100%; + animation-name: spin-end; +} + +@keyframes spin-linear { + 100% { + transform: rotate(360deg); + } +} + +@keyframes spin-swing { + 0% { + transform: rotate(-135deg); + } + 50% { + transform: rotate(0deg); + } + 100% { + transform: rotate(225deg); + } +} + +@keyframes spin-start { + 0%, + 100% { + transform: rotate(0deg); + } + 50% { + transform: rotate(-80deg); + } +} + +@keyframes spin-end { + 0%, + 100% { + transform: rotate(0deg); + } + 50% { + transform: rotate(70deg); + } +} + +@media (forced-colors: active) { + .background { + display: none; + } + .indicator { + border-color: Canvas; + border-block-start-color: Highlight; + border-right-color: Highlight; + } +} diff --git a/packages/web-components/src/spinner/spinner.template.html b/packages/web-components/src/spinner/spinner.template.html new file mode 100644 index 0000000000000..fdd76233d952a --- /dev/null +++ b/packages/web-components/src/spinner/spinner.template.html @@ -0,0 +1,18 @@ + + + diff --git a/packages/web-components/src/switch/switch.styles.css b/packages/web-components/src/switch/switch.styles.css new file mode 100644 index 0000000000000..822b4a1d2908d --- /dev/null +++ b/packages/web-components/src/switch/switch.styles.css @@ -0,0 +1,125 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + box-sizing: border-box; + align-items: center; + flex-direction: row; + outline: none; + user-select: none; + contain: content; + padding: 0 var(--spacingHorizontalXXS); + width: 40px; + height: 20px; + background-color: var(--colorTransparentBackground); + border: 1px solid var(--colorNeutralStrokeAccessible); + border-radius: var(--borderRadiusCircular); +} + +:host(:enabled) { + cursor: pointer; +} + +:host(:hover) { + background: none; + border-color: var(--colorNeutralStrokeAccessibleHover); +} +:host(:active) { + border-color: var(--colorNeutralStrokeAccessiblePressed); +} +:host(:disabled), +:host([readonly]) { + border: 1px solid var(--colorNeutralStrokeDisabled); + background-color: none; + pointer: default; +} +:host(:state(checked)) { + background: var(--colorCompoundBrandBackground); + border-color: var(--colorCompoundBrandBackground); +} +:host(:state(checked):hover) { + background: var(--colorCompoundBrandBackgroundHover); + border-color: var(--colorCompoundBrandBackgroundHover); +} +:host(:state(checked):active) { + background: var(--colorCompoundBrandBackgroundPressed); + border-color: var(--colorCompoundBrandBackgroundPressed); +} +:host(:state(checked):disabled) { + background: var(--colorNeutralBackgroundDisabled); + border-color: var(--colorNeutralStrokeDisabled); +} +.checked-indicator { + height: 14px; + width: 14px; + border-radius: 50%; + margin-inline-start: 0; + background-color: var(--colorNeutralForeground3); + transition-duration: var(--durationNormal); + transition-timing-function: var(--curveEasyEase); + transition-property: margin-inline-start; +} +:host(:state(checked)) .checked-indicator { + background-color: var(--colorNeutralForegroundInverted); + margin-inline-start: calc(100% - 14px); +} +:host(:state(checked):hover) .checked-indicator { + background: var(--colorNeutralForegroundInvertedHover); +} +:host(:state(checked):active) .checked-indicator { + background: var(--colorNeutralForegroundInvertedPressed); +} +:host(:hover) .checked-indicator { + background-color: var(--colorNeutralForeground3Hover); +} +:host(:active) .checked-indicator { + background-color: var(--colorNeutralForeground3Pressed); +} +:host(:disabled) .checked-indicator, +:host([readonly]) .checked-indicator { + background: var(--colorNeutralForegroundDisabled); +} +:host(:state(checked):disabled) .checked-indicator { + background: var(--colorNeutralForegroundDisabled); +} + +:host(:focus-visible) { + outline: none; +} + +:host(:not([slot='input']):focus-visible) { + border-color: var(--colorTransparentStroke); + outline: var(--strokeWidthThick) solid var(--colorTransparentStroke); + outline-offset: 1px; + box-shadow: var(--shadow4), 0 0 0 2px var(--colorStrokeFocus2); +} + +@media (forced-colors: active) { + :host { + border-color: InactiveBorder; + } + :host(:state(checked)), + :host(:state(checked):active), + :host(:state(checked):hover) { + background: Highlight; + border-color: Highlight; + } + .checked-indicator, + :host(:hover) .checked-indicator, + :host(:active) .checked-indicator { + background-color: ActiveCaption; + } + :host(:state(checked)) .checked-indicator, + :host(:state(checked):hover) .checked-indicator, + :host(:state(checked):active) .checked-indicator { + background-color: ButtonFace; + } + :host(:disabled) .checked-indicator, + :host(:state(checked):disabled) .checked-indicator { + background-color: GrayText; + } +} diff --git a/packages/web-components/src/switch/switch.template.html b/packages/web-components/src/switch/switch.template.html new file mode 100644 index 0000000000000..d1c16abd84f01 --- /dev/null +++ b/packages/web-components/src/switch/switch.template.html @@ -0,0 +1,11 @@ + + + diff --git a/packages/web-components/src/tab/tab.styles.css b/packages/web-components/src/tab/tab.styles.css new file mode 100644 index 0000000000000..a22fc5f5173a1 --- /dev/null +++ b/packages/web-components/src/tab/tab.styles.css @@ -0,0 +1,122 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + position: relative; + flex-direction: row; + align-items: center; + cursor: pointer; + box-sizing: border-box; + justify-content: center; + line-height: var(--lineHeightBase300); + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + color: var(--colorNeutralForeground2); + fill: currentcolor; + grid-row: 1; + padding: var(--spacingHorizontalM) var(--spacingHorizontalMNudge); + border-radius: var(--borderRadiusMedium); + gap: 4px; +} +:host .tab-content { + display: inline-flex; + flex-direction: column; + padding: 0 2px; +} + +:host([aria-selected='true']) { + color: var(--colorNeutralForeground1); + font-weight: var(--fontWeightSemibold); +} + +/* adds hidden textContent to prevent shifting ui on bold / unbolding of text */ +:host .tab-content::after { + content: var(--textContent); + visibility: hidden; + height: 0; + line-height: var(--lineHeightBase300); + font-weight: var(--fontWeightSemibold); +} + +:host([aria-selected='true'])::after { + background-color: var(--colorCompoundBrandStroke); + border-radius: var(--borderRadiusCircular); + content: ''; + inset: 0; + position: absolute; + z-index: 2; +} + +:host([aria-selected='false']:hover)::after { + background-color: var(--colorNeutralStroke1Hover); + border-radius: var(--borderRadiusCircular); + content: ''; + inset: 0; + position: absolute; + z-index: 1; +} + +/* + * TODO: Remove '(text-size-adjust: auto)' after this bug is fixed: + * https://bugs.webkit.org/show_bug.cgi?id=298646 + * Also remove the same trick from tablist.styles.ts. + * Using '@supports (text-size-adjust: auto)' here to exclude Safari 26 from + * using CSS Anchor Positioning here because it crashes. + */ +@supports (anchor-name: --a) and (text-size-adjust: auto) { + :host([aria-selected='true'])::after { + background-color: transparent; + } + + :host([aria-selected='true']:hover)::after { + background-color: var(--colorNeutralStroke1Hover); + } +} + +:host([aria-selected='true'][disabled])::after { + background-color: var(--colorNeutralForegroundDisabled); +} + +::slotted([slot='start']), +::slotted([slot='end']) { + display: flex; +} +:host([disabled]) { + cursor: not-allowed; + fill: var(--colorNeutralForegroundDisabled); + color: var(--colorNeutralForegroundDisabled); + pointer-events: none; +} + +:host([disabled]:hover)::after { + background-color: unset; +} + +:host(:focus) { + outline: none; +} + +:host(:focus-visible) { + border-radius: var(--borderRadiusSmall); + box-shadow: 0 0 0 3px var(--colorStrokeFocus2); + outline: 1px solid var(--colorStrokeFocus1); +} + +:host([data-hasIndent]) { + display: grid; + grid-template-columns: 20px 1fr auto; +} + +:host([data-hasIndent]) .tab-content { + grid-column: 2; +} + +@media (forced-colors: active) { + :host([aria-selected='true'])::after { + background-color: Highlight; + } +} diff --git a/packages/web-components/src/tab/tab.template.html b/packages/web-components/src/tab/tab.template.html new file mode 100644 index 0000000000000..94f56b8b92420 --- /dev/null +++ b/packages/web-components/src/tab/tab.template.html @@ -0,0 +1,8 @@ + + + diff --git a/packages/web-components/src/tablist/tablist.styles.css b/packages/web-components/src/tablist/tablist.styles.css new file mode 100644 index 0000000000000..ad2a25c1f36a3 --- /dev/null +++ b/packages/web-components/src/tablist/tablist.styles.css @@ -0,0 +1,207 @@ +:host([hidden]) { + display: none; +} +:host { + display: flex; +} + +:host { + --tabPaddingInline: var(--spacingHorizontalMNudge); + --tabPaddingBlock: var(--spacingHorizontalM); + --tabIndicatorInsetInline: var(--tabPaddingInline); + --tabIndicatorInsetBlock: 0; + box-sizing: border-box; + color: var(--colorNeutralForeground2); + flex-direction: row; + position: relative; +} + +:host([size='small']) { + --tabPaddingBlock: var(--spacingVerticalSNudge); + --tabPaddingInline: var(--spacingHorizontalSNudge); +} + +:host([size='large']) { + --tabPaddingBlock: var(--spacingVerticalL); + --tabPaddingInline: var(--spacingHorizontalMNudge); +} + +:host([orientation='vertical']) { + --tabPaddingBlock: var(--spacingVerticalS); + --tabIndicatorInsetBlock: var(--spacingVerticalS); + flex-direction: column; +} + +:host([orientation='vertical'][size='small']) { + --tabPaddingBlock: var(--spacingVerticalXXS); + --tabIndicatorInsetBlock: var(--spacingVerticalSNudge); +} + +:host([orientation='vertical'][size='large']) { + --tabPaddingBlock: var(--spacingVerticalS); + --tabIndicatorInsetBlock: var(--spacingVerticalMNudge); +} + +::slotted([slot='tab']) { + padding-inline: var(--tabPaddingInline); + padding-block: var(--tabPaddingBlock); +} + +:host([orientation='vertical']) ::slotted([role='tab']) { + justify-content: flex-start; +} + +:host ::slotted([slot='tab'])::after { + height: var(--strokeWidthThicker); + margin-block-start: auto; +} + +:host([orientation='vertical']) ::slotted([slot='tab'])::after { + width: var(--strokeWidthThicker); + height: unset; + margin-block-start: unset; +} + +/* ::before adds a secondary indicator placeholder that appears right after click on the active tab */ +:host ::slotted([slot='tab'])::before { + height: var(--strokeWidthThicker); + border-radius: var(--borderRadiusCircular); + content: ''; + inset-inline: var(--tabIndicatorInsetInline); + inset-block: var(--tabIndicatorInsetBlock); + position: absolute; + margin-top: auto; +} + +:host ::slotted([slot='tab'])::before { + inset-inline: var(--tabIndicatorInsetInline); + inset-block: var(--tabIndicatorInsetBlock); +} + +:host ::slotted([slot='tab'][aria-selected='true'])::before { + background-color: var(--colorNeutralForegroundDisabled); +} + +:host ::slotted([slot='tab'][aria-selected='false']:hover)::after { + height: var(--strokeWidthThicker); + margin-block-start: auto; + transform-origin: left; +} + +:host([orientation='vertical']) ::slotted([slot='tab'])::before, +:host([orientation='vertical']) ::slotted([slot='tab'][aria-selected='false']:hover)::after { + height: unset; + width: var(--strokeWidthThicker); + margin-inline-end: auto; + transform-origin: top; +} + +:host([size='small']) ::slotted([slot='tab']) { + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); +} + +:host([size='large']) ::slotted([slot='tab']) { + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} + +/* horizontal spacing for indicator */ +:host ::slotted([slot='tab'])::after, +:host ::slotted([slot='tab'])::before, +:host ::slotted([slot='tab']:hover)::after { + inset-inline: var(--tabIndicatorInsetInline); +} + +:host([orientation='vertical']) ::slotted([slot='tab'])::after, +:host([orientation='vertical']) ::slotted([slot='tab'])::before, +:host([orientation='vertical']) ::slotted([slot='tab']:hover)::after { + inset-inline: 0; + inset-block: var(--tabIndicatorInsetBlock); +} + +/* disabled styles */ +:host([disabled]) { + cursor: not-allowed; + color: var(--colorNeutralForegroundDisabled); +} + +:host([disabled]) ::slotted([slot='tab']) { + pointer-events: none; + cursor: not-allowed; + color: var(--colorNeutralForegroundDisabled); +} + +:host([disabled]) ::slotted([slot='tab']:after) { + background-color: var(--colorNeutralForegroundDisabled); +} + +:host([disabled]) ::slotted([slot='tab'][aria-selected='true'])::after { + background-color: var(--colorNeutralForegroundDisabled); +} + +:host([disabled]) ::slotted([slot='tab']:hover):before { + content: unset; +} + +:host([appearance='subtle']) ::slotted([slot='tab']:hover) { + background-color: var(--colorSubtleBackgroundHover); + color: var(--colorNeutralForeground1Hover); + fill: var(--colorCompoundBrandForeground1Hover); +} + +:host([appearance='subtle']) ::slotted([slot='tab']:active) { + background-color: var(--colorSubtleBackgroundPressed); + fill: var(--colorSubtleBackgroundPressed); + color: var(--colorNeutralForeground1); +} + +/* + * TODO: Remove '(text-size-adjust: auto)' after this bug is fixed: + * https://bugs.webkit.org/show_bug.cgi?id=298646 + * Also remove the same trick from tab.styles.ts. + * Using '@supports (text-size-adjust: auto)' here to exclude Safari 26 from + * using CSS Anchor Positioning here because it crashes. + */ +@supports (anchor-name: --a) and (text-size-adjust: auto) { + ::slotted([slot='tab'][aria-selected='true']) { + anchor-name: --tab; + } + + :host::after { + background-color: var(--colorCompoundBrandStroke); + content: ''; + inline-size: 100%; + inset: auto auto anchor(end) anchor(center); + position: absolute; + position-anchor: --tab; + transform: translateX(-50%); + transition-property: inset-inline, width; + transition-duration: var(--durationSlow); + transition-timing-function: var(--curveDecelerateMax); + z-index: 3; + + /* These styles should be in sync with tab.styles.ts’s :host::after */ + border-radius: var(--borderRadiusCircular); + width: calc(anchor-size() - var(--tabIndicatorInsetInline) * 2); + height: var(--strokeWidthThicker); + } + + :host([orientation='vertical'])::after { + inset: anchor(center) anchor(end) auto 0; + transform: translateY(-50%); + transition-property: inset-block, height; + + /* These styles should be in sync with #vertical-tab-highlight above */ + width: var(--strokeWidthThicker); + height: calc(anchor-size() - var(--tabIndicatorInsetBlock) * 2); + } + + :host(:dir(rtl)[orientation='vertical'])::after { + inset: anchor(center) anchor(start) auto 0; + } + + :host([disabled])::after { + background-color: var(--colorNeutralForegroundDisabled); + } +} diff --git a/packages/web-components/src/tablist/tablist.template.html b/packages/web-components/src/tablist/tablist.template.html new file mode 100644 index 0000000000000..f436038c4e8ab --- /dev/null +++ b/packages/web-components/src/tablist/tablist.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/text-input/text-input.styles.css b/packages/web-components/src/text-input/text-input.styles.css new file mode 100644 index 0000000000000..9a6e4d09ee3b8 --- /dev/null +++ b/packages/web-components/src/text-input/text-input.styles.css @@ -0,0 +1,204 @@ +:host([hidden]) { + display: none; +} +:host { + display: block; +} + +:host { + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + font-weight: var(--fontWeightRegular); + line-height: var(--lineHeightBase300); + max-width: 400px; +} +.label { + display: flex; + color: var(--colorNeutralForeground1); + padding-bottom: var(--spacingVerticalXS); + flex-shrink: 0; + padding-inline-end: var(--spacingHorizontalXS); +} + +.label[hidden], +:host(:empty) .label { + display: none; +} + +.root { + align-items: center; + background-color: var(--colorNeutralBackground1); + border: var(--strokeWidthThin) solid var(--colorNeutralStroke1); + border-bottom-color: var(--colorNeutralStrokeAccessible); + border-radius: var(--borderRadiusMedium); + box-sizing: border-box; + height: 32px; + display: inline-flex; + flex-direction: row; + gap: var(--spacingHorizontalXXS); + padding: 0 var(--spacingHorizontalMNudge); + position: relative; + width: 100%; +} + +:has(.control:user-invalid) { + border-color: var(--colorPaletteRedBorder2); +} + +.root::after { + box-sizing: border-box; + content: ''; + position: absolute; + left: -1px; + bottom: 0px; + right: -1px; + height: max(2px, var(--borderRadiusMedium)); + border-radius: 0 0 var(--borderRadiusMedium) var(--borderRadiusMedium); + border-bottom: 2px solid var(--colorCompoundBrandStroke); + clip-path: inset(calc(100% - 2px) 1px 0px); + transform: scaleX(0); + transition-property: transform; + transition-duration: var(--durationUltraFast); + transition-delay: var(--curveAccelerateMid); +} +.control { + width: 100%; + height: 100%; + box-sizing: border-box; + color: var(--colorNeutralForeground1); + border-radius: var(--borderRadiusMedium); + background: var(--colorTransparentBackground); + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightRegular); + font-size: var(--fontSizeBase300); + border: none; + vertical-align: center; +} +.control:focus-visible { + outline: 0; + border: 0; +} +.control::placeholder { + color: var(--colorNeutralForeground4); +} +:host ::slotted([slot='start']), +:host ::slotted([slot='end']) { + display: flex; + align-items: center; + justify-content: center; + color: var(--colorNeutralForeground3); + font-size: var(--fontSizeBase500); +} +:host ::slotted([slot='start']) { + padding-right: var(--spacingHorizontalXXS); +} +:host ::slotted([slot='end']) { + padding-left: var(--spacingHorizontalXXS); + gap: var(--spacingHorizontalXS); +} +:host(:hover) .root { + border-color: var(--colorNeutralStroke1Hover); + border-bottom-color: var(--colorNeutralStrokeAccessibleHover); +} +:host(:active) .root { + border-color: var(--colorNeutralStroke1Pressed); +} +:host(:focus-within) .root { + outline: transparent solid 2px; + border-bottom: 0; +} +:host(:focus-within) .root::after { + transform: scaleX(1); + transition-property: transform; + transition-duration: var(--durationNormal); + transition-delay: var(--curveDecelerateMid); +} +:host(:focus-within:active) .root:after { + border-bottom-color: var(--colorCompoundBrandStrokePressed); +} +:host([appearance='outline']:focus-within) .root { + border: var(--strokeWidthThin) solid var(--colorNeutralStroke1); +} +:host(:focus-within) .control { + color: var(--colorNeutralForeground1); +} +:host([disabled]) .root { + background: var(--colorTransparentBackground); + border: var(--strokeWidthThin) solid var(--colorNeutralStrokeDisabled); +} +:host([disabled]) .control::placeholder, +:host([disabled]) ::slotted([slot='start']), +:host([disabled]) ::slotted([slot='end']) { + color: var(--colorNeutralForegroundDisabled); +} +::selection { + color: var(--colorNeutralForegroundInverted); + background-color: var(--colorNeutralBackgroundInverted); +} +:host([control-size='small']) .control { + font-size: var(--fontSizeBase200); + font-weight: var(--fontWeightRegular); + line-height: var(--lineHeightBase200); +} +:host([control-size='small']) .root { + height: 24px; + gap: var(--spacingHorizontalXXS); + padding: 0 var(--spacingHorizontalSNudge); +} +:host([control-size='small']) ::slotted([slot='start']), +:host([control-size='small']) ::slotted([slot='end']) { + font-size: var(--fontSizeBase400); +} +:host([control-size='large']) .control { + font-size: var(--fontSizeBase400); + font-weight: var(--fontWeightRegular); + line-height: var(--lineHeightBase400); +} +:host([control-size='large']) .root { + height: 40px; + gap: var(--spacingHorizontalS); + padding: 0 var(--spacingHorizontalM); +} +:host([control-size='large']) ::slotted([slot='start']), +:host([control-size='large']) ::slotted([slot='end']) { + font-size: var(--fontSizeBase600); +} +:host([appearance='underline']) .root { + background: var(--colorTransparentBackground); + border: 0; + border-radius: 0; + border-bottom: var(--strokeWidthThin) solid var(--colorNeutralStrokeAccessible); +} +:host([appearance='underline']:hover) .root { + border-bottom-color: var(--colorNeutralStrokeAccessibleHover); +} +:host([appearance='underline']:active) .root { + border-bottom-color: var(--colorNeutralStrokeAccessiblePressed); +} +:host([appearance='underline']:focus-within) .root { + border: 0; + border-bottom-color: var(--colorNeutralStrokeAccessiblePressed); +} +:host([appearance='underline'][disabled]) .root { + border-bottom-color: var(--colorNeutralStrokeDisabled); +} +:host([appearance='filled-lighter']) .root, +:host([appearance='filled-darker']) .root { + border: var(--strokeWidthThin) solid var(--colorTransparentStroke); + box-shadow: var(--shadow2); +} +:host([appearance='filled-lighter']) .root { + background: var(--colorNeutralBackground1); +} +:host([appearance='filled-darker']) .root { + background: var(--colorNeutralBackground3); +} +:host([appearance='filled-lighter']:hover) .root, +:host([appearance='filled-darker']:hover) .root { + border-color: var(--colorTransparentStrokeInteractive); +} +:host([appearance='filled-lighter']:active) .root, +:host([appearance='filled-darker']:active) .root { + border-color: var(--colorTransparentStrokeInteractive); + background: var(--colorNeutralBackground3); +} diff --git a/packages/web-components/src/text-input/text-input.template.html b/packages/web-components/src/text-input/text-input.template.html new file mode 100644 index 0000000000000..838b1bd2c548a --- /dev/null +++ b/packages/web-components/src/text-input/text-input.template.html @@ -0,0 +1,36 @@ + + + diff --git a/packages/web-components/src/text/text.styles.css b/packages/web-components/src/text/text.styles.css new file mode 100644 index 0000000000000..8320322aea1bf --- /dev/null +++ b/packages/web-components/src/text/text.styles.css @@ -0,0 +1,108 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline; +} + +:host { + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); + font-weight: var(--fontWeightRegular); + text-align: start; +} + +:host([nowrap]), +:host([nowrap]) ::slotted(*) { + white-space: nowrap; + overflow: hidden; +} +:host([truncate]), +:host([truncate]) ::slotted(*) { + text-overflow: ellipsis; +} +:host([block]) { + display: block; +} +:host([italic]) { + font-style: italic; +} +:host([underline]) { + text-decoration-line: underline; +} +:host([strikethrough]) { + text-decoration-line: line-through; +} +:host([underline][strikethrough]) { + text-decoration-line: line-through underline; +} +:host([size='100']) { + font-size: var(--fontSizeBase100); + line-height: var(--lineHeightBase100); +} +:host([size='200']) { + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); +} +:host([size='400']) { + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} +:host([size='500']) { + font-size: var(--fontSizeBase500); + line-height: var(--lineHeightBase500); +} +:host([size='600']) { + font-size: var(--fontSizeBase600); + line-height: var(--lineHeightBase600); +} +:host([size='700']) { + font-size: var(--fontSizeHero700); + line-height: var(--lineHeightHero700); +} +:host([size='800']) { + font-size: var(--fontSizeHero800); + line-height: var(--lineHeightHero800); +} +:host([size='900']) { + font-size: var(--fontSizeHero900); + line-height: var(--lineHeightHero900); +} +:host([size='1000']) { + font-size: var(--fontSizeHero1000); + line-height: var(--lineHeightHero1000); +} +:host([font='monospace']) { + font-family: var(--fontFamilyMonospace); +} +:host([font='numeric']) { + font-family: var(--fontFamilyNumeric); +} +:host([weight='medium']) { + font-weight: var(--fontWeightMedium); +} +:host([weight='semibold']) { + font-weight: var(--fontWeightSemibold); +} +:host([weight='bold']) { + font-weight: var(--fontWeightBold); +} +:host([align='center']) { + text-align: center; +} +:host([align='end']) { + text-align: end; +} +:host([align='justify']) { + text-align: justify; +} + +::slotted(*) { + font: inherit; + line-height: inherit; + text-decoration-line: inherit; + text-align: inherit; + text-decoration-line: inherit; + margin: 0; +} diff --git a/packages/web-components/src/text/text.template.html b/packages/web-components/src/text/text.template.html new file mode 100644 index 0000000000000..1985e9c8cccac --- /dev/null +++ b/packages/web-components/src/text/text.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/textarea/textarea.styles.css b/packages/web-components/src/textarea/textarea.styles.css new file mode 100644 index 0000000000000..dd49fb6e1b065 --- /dev/null +++ b/packages/web-components/src/textarea/textarea.styles.css @@ -0,0 +1,262 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-block; +} + +:host { + /* typography */ + --font-size: var(--fontSizeBase300); + --line-height: var(--lineHeightBase300); + + /* layout */ + --padding-inline: var(--spacingHorizontalMNudge); + --padding-block: var(--spacingVerticalSNudge); + --min-block-size: 52px; + --block-size: var(--min-block-size); + --inline-size: 18rem; + --border-width: var(--strokeWidthThin); + --control-padding-inline: var(--spacingHorizontalXXS); + + /* colors */ + --color: var(--colorNeutralForeground1); + --background-color: var(--colorNeutralBackground1); + --border-color: var(--colorNeutralStroke1); + --border-block-end-color: var(--colorNeutralStrokeAccessible); + --placeholder-color: var(--colorNeutralForeground4); + --focus-indicator-color: var(--colorCompoundBrandStroke); + + /* elevations */ + --box-shadow: none; + + /* others */ + --contain-size: size; + --resize: none; + + color: var(--color); + font-family: var(--fontFamilyBase); + font-size: var(--font-size); + font-weight: var(--fontWeightRegular); + line-height: var(--line-height); + position: relative; +} + +:host(:hover) { + --border-color: var(--colorNeutralStroke1Hover); + --border-block-end-color: var(--colorNeutralStrokeAccessibleHover); +} + +:host(:active) { + --border-color: var(--colorNeutralStroke1Pressed); + --border-block-end-color: var(--colorNeutralStrokeAccessiblePressed); +} + +:host(:focus-within) { + outline: none; +} + +:host([block]:not([hidden])) { + display: block; +} + +:host([size='small']) { + --font-size: var(--fontSizeBase200); + --line-height: var(--lineHeightBase200); + --min-block-size: 40px; + --padding-block: var(--spacingVerticalXS); + --padding-inline: var(--spacingHorizontalSNudge); + --control-padding-inline: var(--spacingHorizontalXXS); +} + +:host([size='large']) { + --font-size: var(--fontSizeBase400); + --line-height: var(--lineHeightBase400); + --min-block-size: 64px; + --padding-block: var(--spacingVerticalS); + --padding-inline: var(--spacingHorizontalM); + --control-padding-inline: var(--spacingHorizontalSNudge); +} + +:host([resize='both']:not(:disabled)) { + --resize: both; +} + +:host([resize='horizontal']:not(:disabled)) { + --resize: horizontal; +} + +:host([resize='vertical']:not(:disabled)) { + --resize: vertical; +} + +:host([auto-resize]) { + --block-size: auto; + --contain-size: inline-size; +} + +:host([appearance='filled-darker']) { + --background-color: var(--colorNeutralBackground3); + --border-color: var(--background-color); + --border-block-end-color: var(--border-color); +} + +:host([appearance='filled-lighter']) { + --border-color: var(--background-color); + --border-block-end-color: var(--border-color); +} + +:host([appearance='filled-darker'][display-shadow]), +:host([appearance='filled-lighter'][display-shadow]) { + --box-shadow: var(--shadow2); +} + +:host(:state(user-invalid)) { + --border-color: var(--colorPaletteRedBorder2); + --border-block-end-color: var(--colorPaletteRedBorder2); +} + +:host(:disabled) { + --color: var(--colorNeutralForegroundDisabled); + --background-color: var(--colorTransparentBackground); + --border-color: var(--colorNeutralStrokeDisabled); + --border-block-end-color: var(--border-color); + --box-shadow: none; + --placeholder-color: var(--colorNeutralForegroundDisabled); + + cursor: no-drop; + user-select: none; +} + +.root { + background-color: var(--background-color); + border: var(--border-width) solid var(--border-color); + border-block-end-color: var(--border-block-end-color); + border-radius: var(--borderRadiusMedium); + box-sizing: border-box; + box-shadow: var(--box-shadow); + contain: paint layout style var(--contain-size); + display: grid; + grid-template: 1fr / 1fr; + inline-size: var(--inline-size); + min-block-size: var(--min-block-size); + block-size: var(--block-size); + overflow: hidden; + padding: var(--padding-block) var(--padding-inline); + position: relative; + resize: var(--resize); +} + +:host([block]) .root { + inline-size: auto; +} + +.root::after { + border-bottom: 2px solid var(--focus-indicator-color); + border-radius: 0 0 var(--borderRadiusMedium) var(--borderRadiusMedium); + box-sizing: border-box; + clip-path: inset(calc(100% - 2px) 1px 0px); + content: ''; + height: max(2px, var(--borderRadiusMedium)); + inset: auto -1px 0; + position: absolute; + transform: scaleX(0); + transition-delay: var(--curveAccelerateMid); + transition-duration: var(--durationUltraFast); + transition-property: transform; +} + +:host(:focus-within) .root::after { + transform: scaleX(1); + transition-property: transform; + transition-duration: var(--durationNormal); + transition-delay: var(--curveDecelerateMid); +} + +:host([readonly]) .root::after, +:host(:disabled) .root::after { + content: none; +} + +label { + color: var(--color); + display: flex; + inline-size: fit-content; + padding-block-end: var(--spacingVerticalXS); + padding-inline-end: var(--spacingHorizontalXS); +} + +:host(:empty) label, +label[hidden] { + display: none; +} + +.auto-sizer, +.control { + box-sizing: border-box; + font: inherit; + grid-column: 1 / -1; + grid-row: 1 / -1; + letter-space: inherit; + padding: 0 var(--control-padding-inline); +} + +.auto-sizer { + display: none; + padding-block-end: 2px; /* avoid scroll bar in Firefox */ + pointer-events: none; + visibility: hidden; + white-space: pre-wrap; +} + +:host([auto-resize]) .auto-sizer { + display: block; +} + +.control { + appearance: none; + background-color: transparent; + border: 0; + color: inherit; + field-sizing: content; + max-block-size: 100%; + outline: 0; + resize: none; + text-align: inherit; +} + +.control:disabled { + cursor: inherit; +} + +.control::placeholder { + color: var(--placeholder-color); +} + +::selection { + color: var(--colorNeutralForegroundInverted); + background-color: var(--colorNeutralBackgroundInverted); +} + +@media (forced-colors: active) { + :host { + --border-color: FieldText; + --border-block-end-color: FieldText; + --focus-indicator-color: Highlight; + --placeholder-color: FieldText; + } + + :host(:hover), + :host(:active), + :host(:focus-within) { + --border-color: Highlight; + --border-block-end-color: Highlight; + } + + :host(:disabled) { + --color: GrayText; + --border-color: GrayText; + --border-block-end-color: GrayText; + --placeholder-color: GrayText; + } +} diff --git a/packages/web-components/src/textarea/textarea.template.html b/packages/web-components/src/textarea/textarea.template.html new file mode 100644 index 0000000000000..83216b4cecdf6 --- /dev/null +++ b/packages/web-components/src/textarea/textarea.template.html @@ -0,0 +1,30 @@ + + + diff --git a/packages/web-components/src/toggle-button/toggle-button.styles.css b/packages/web-components/src/toggle-button/toggle-button.styles.css new file mode 100644 index 0000000000000..adf5b9833e884 --- /dev/null +++ b/packages/web-components/src/toggle-button/toggle-button.styles.css @@ -0,0 +1,361 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host { + --icon-spacing: var(--spacingHorizontalSNudge); + position: relative; + contain: layout style; + vertical-align: middle; + align-items: center; + box-sizing: border-box; + justify-content: center; + text-align: center; + text-decoration-line: none; + margin: 0; + min-height: 32px; + outline-style: none; + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground1); + border: var(--strokeWidthThin) solid var(--colorNeutralStroke1); + padding: 0 var(--spacingHorizontalM); + min-width: 96px; + border-radius: var(--borderRadiusMedium); + font-size: var(--fontSizeBase300); + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + line-height: var(--lineHeightBase300); + transition-duration: var(--durationFaster); + transition-property: background, border, color; + transition-timing-function: var(--curveEasyEase); + cursor: pointer; + user-select: none; +} + +.content { + display: inherit; +} + +:host(:hover) { + background-color: var(--colorNeutralBackground1Hover); + color: var(--colorNeutralForeground1Hover); + border-color: var(--colorNeutralStroke1Hover); +} + +:host(:hover:active) { + background-color: var(--colorNeutralBackground1Pressed); + border-color: var(--colorNeutralStroke1Pressed); + color: var(--colorNeutralForeground1Pressed); + outline-style: none; +} + +:host(:focus-visible) { + border-color: var(--colorTransparentStroke); + outline: var(--strokeWidthThick) solid var(--colorTransparentStroke); + box-shadow: var(--shadow4), 0 0 0 2px var(--colorStrokeFocus2); +} + +@media screen and (prefers-reduced-motion: reduce) { + :host { + transition-duration: 0.01ms; + } +} + +::slotted(svg) { + font-size: 20px; + height: 20px; + width: 20px; + fill: currentColor; +} + +::slotted([slot='start']) { + margin-inline-end: var(--icon-spacing); +} + +::slotted([slot='end']), +[slot='end'] { + flex-shrink: 0; + margin-inline-start: var(--icon-spacing); +} + +:host([icon-only]) { + min-width: 32px; + max-width: 32px; +} + +:host([size='small']) { + --icon-spacing: var(--spacingHorizontalXS); + min-height: 24px; + min-width: 64px; + padding: 0 var(--spacingHorizontalS); + border-radius: var(--borderRadiusSmall); + font-size: var(--fontSizeBase200); + line-height: var(--lineHeightBase200); + font-weight: var(--fontWeightRegular); +} + +:host([size='small'][icon-only]) { + min-width: 24px; + max-width: 24px; +} + +:host([size='large']) { + min-height: 40px; + border-radius: var(--borderRadiusLarge); + padding: 0 var(--spacingHorizontalL); + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); +} + +:host([size='large'][icon-only]) { + min-width: 40px; + max-width: 40px; +} + +:host([size='large']) ::slotted(svg) { + font-size: 24px; + height: 24px; + width: 24px; +} + +:host(:is([shape='circular'], [shape='circular']:focus-visible)) { + border-radius: var(--borderRadiusCircular); +} + +:host(:is([shape='square'], [shape='square']:focus-visible)) { + border-radius: var(--borderRadiusNone); +} + +:host([appearance='primary']) { + background-color: var(--colorBrandBackground); + color: var(--colorNeutralForegroundOnBrand); + border-color: transparent; +} + +:host([appearance='primary']:hover) { + background-color: var(--colorBrandBackgroundHover); +} + +:host([appearance='primary']:is(:hover, :hover:active):not(:focus-visible)) { + border-color: transparent; +} + +:host([appearance='primary']:is(:hover, :hover:active)) { + color: var(--colorNeutralForegroundOnBrand); +} + +:host([appearance='primary']:hover:active) { + background-color: var(--colorBrandBackgroundPressed); +} + +:host([appearance='primary']:focus-visible) { + border-color: var(--colorNeutralForegroundOnBrand); + box-shadow: var(--shadow2), 0 0 0 2px var(--colorStrokeFocus2); +} + +:host([appearance='outline']) { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='outline']:hover) { + background-color: var(--colorTransparentBackgroundHover); +} + +:host([appearance='outline']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); +} + +:host([appearance='subtle']) { + background-color: var(--colorSubtleBackground); + color: var(--colorNeutralForeground2); + border-color: transparent; +} + +:host([appearance='subtle']:hover) { + background-color: var(--colorSubtleBackgroundHover); + color: var(--colorNeutralForeground2Hover); + border-color: transparent; +} + +:host([appearance='subtle']:hover:active) { + background-color: var(--colorSubtleBackgroundPressed); + color: var(--colorNeutralForeground2Pressed); + border-color: transparent; +} + +:host([appearance='subtle']:hover) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='subtle']:hover:active) ::slotted(svg) { + fill: var(--colorNeutralForeground2BrandPressed); +} + +:host([appearance='transparent']) { + background-color: var(--colorTransparentBackground); + color: var(--colorNeutralForeground2); +} + +:host([appearance='transparent']:hover) { + background-color: var(--colorTransparentBackgroundHover); + color: var(--colorNeutralForeground2BrandHover); +} + +:host([appearance='transparent']:hover:active) { + background-color: var(--colorTransparentBackgroundPressed); + color: var(--colorNeutralForeground2BrandPressed); +} + +:host(:is([appearance='transparent'], [appearance='transparent']:is(:hover, :active))) { + border-color: transparent; +} + +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])), +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable]):hover), +:host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable]):hover:active) { + background-color: var(--colorNeutralBackgroundDisabled); + border-color: var(--colorNeutralStrokeDisabled); + color: var(--colorNeutralForegroundDisabled); + cursor: not-allowed; +} + +:host([appearance='primary']:is(:disabled, [disabled-focusable])), +:host([appearance='primary']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + border-color: transparent; +} + +:host([appearance='outline']:is(:disabled, [disabled-focusable])), +:host([appearance='outline']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='subtle']:is(:disabled, [disabled-focusable])), +:host([appearance='subtle']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + background-color: var(--colorTransparentBackground); + border-color: transparent; +} + +:host([appearance='transparent']:is(:disabled, [disabled-focusable])), +:host([appearance='transparent']:is(:disabled, [disabled-focusable]):is(:hover, :hover:active)) { + border-color: transparent; + background-color: var(--colorTransparentBackground); +} + +@media (forced-colors: active) { + :host { + background-color: ButtonFace; + color: ButtonText; + } + + :host(:is(:hover, :focus-visible)) { + border-color: Highlight !important; + } + + :host([appearance='primary']:not(:is(:hover, :focus-visible))) { + background-color: Highlight; + color: HighlightText; + forced-color-adjust: none; + } + + :host(:is(:disabled, [disabled-focusable], [appearance]:disabled, [appearance][disabled-focusable])) { + background-color: ButtonFace; + color: GrayText; + border-color: ButtonText; + } +} + +:host(:state(pressed)) { + border-color: var(--colorNeutralStroke1); + background-color: var(--colorNeutralBackground1Selected); + color: var(--colorNeutralForeground1); + border-width: var(--strokeWidthThin); +} + +:host(:state(pressed):hover) { + border-color: var(--colorNeutralStroke1Hover); + background-color: var(--colorNeutralBackground1Hover); +} + +:host(:state(pressed):active) { + border-color: var(--colorNeutralStroke1Pressed); + background-color: var(--colorNeutralBackground1Pressed); +} + +:host(:state(pressed)[appearance='primary']:not(:focus-visible)) { + border-color: transparent; +} + +:host(:state(pressed)[appearance='primary']) { + background-color: var(--colorBrandBackgroundSelected); + color: var(--colorNeutralForegroundOnBrand); +} + +:host(:state(pressed)[appearance='primary']:hover) { + background-color: var(--colorBrandBackgroundHover); +} + +:host(:state(pressed)[appearance='primary']:active) { + background-color: var(--colorBrandBackgroundPressed); +} + +:host(:state(pressed)[appearance='subtle']) { + border-color: transparent; + background-color: var(--colorSubtleBackgroundSelected); + color: var(--colorNeutralForeground2Selected); +} + +:host(:state(pressed)[appearance='subtle']:hover) { + background-color: var(--colorSubtleBackgroundHover); + color: var(--colorNeutralForeground2Hover); +} + +:host(:state(pressed)[appearance='subtle']:active) { + background-color: var(--colorSubtleBackgroundPressed); + color: var(--colorNeutralForeground2Pressed); +} + +:host(:state(pressed)[appearance='outline']), +:host(:state(pressed)[appearance='transparent']) { + background-color: var(--colorTransparentBackgroundSelected); +} + +:host(:state(pressed)[appearance='outline']:hover), +:host(:state(pressed)[appearance='transparent']:hover) { + background-color: var(--colorTransparentBackgroundHover); +} + +:host(:state(pressed)[appearance='outline']:active), +:host(:state(pressed)[appearance='transparent']:active) { + background-color: var(--colorTransparentBackgroundPressed); +} + +:host(:state(pressed)[appearance='transparent']) { + border-color: transparent; + color: var(--colorNeutralForeground2BrandSelected); +} + +:host(:state(pressed)[appearance='transparent']:hover) { + color: var(--colorNeutralForeground2BrandHover); +} + +:host(:state(pressed)[appearance='transparent']:active) { + color: var(--colorNeutralForeground2BrandPressed); +} + +@media (forced-colors: active) { + :host(:state(pressed)), + :host( + :state(pressed):is( + [appearance='primary'], + [appearance='subtle'], + [appearance='outline'], + [appearance='transparent'] + ) + ) { + background: SelectedItem; + color: SelectedItemText; + } +} diff --git a/packages/web-components/src/toggle-button/toggle-button.template.html b/packages/web-components/src/toggle-button/toggle-button.template.html new file mode 100644 index 0000000000000..c95388c7c2b0a --- /dev/null +++ b/packages/web-components/src/toggle-button/toggle-button.template.html @@ -0,0 +1,10 @@ + + + diff --git a/packages/web-components/src/tooltip/tooltip.styles.css b/packages/web-components/src/tooltip/tooltip.styles.css new file mode 100644 index 0000000000000..8f575709f5879 --- /dev/null +++ b/packages/web-components/src/tooltip/tooltip.styles.css @@ -0,0 +1,90 @@ +:host([hidden]) { + display: none; +} +:host { + display: inline-flex; +} + +:host(:not(:popover-open)) { + display: none; +} + +:host { + --position-area: block-start; + --position-try-options: flip-block; + --block-offset: var(--spacingVerticalXS); + --inline-offset: var(--spacingHorizontalXS); + background: var(--colorNeutralBackground1); + border-radius: var(--borderRadiusMedium); + border: 1px solid var(--colorTransparentStroke); + box-sizing: border-box; + color: var(--colorNeutralForeground1); + display: inline-flex; + filter: drop-shadow(0 0 2px var(--colorNeutralShadowAmbient)) drop-shadow(0 4px 8px var(--colorNeutralShadowKey)); + font-family: var(--fontFamilyBase); + font-size: var(--fontSizeBase200); + inset: unset; + line-height: var(--lineHeightBase200); + margin: unset; /* Remove browser default for [popover] */ + max-width: 240px; + overflow: visible; + padding: 4px var(--spacingHorizontalMNudge) 6px; + position: absolute; + position-area: var(--position-area); + position-try-options: var(--position-try-options); + width: auto; + z-index: 1; +} + +@supports (inset-area: block-start) { + :host { + inset-area: var(--position-area); + position-try-fallbacks: var(--position-try-options); + } +} + +:host(:is([positioning^='above'], [positioning^='below'], :not([positioning]))) { + margin-block: var(--block-offset); +} + +:host(:is([positioning^='before'], [positioning^='after'])) { + margin-inline: var(--inline-offset); + --position-try-options: flip-inline; +} + +:host([positioning='above-start']) { + --position-area: block-start span-inline-end; +} +:host([positioning='above']) { + --position-area: block-start; +} +:host([positioning='above-end']) { + --position-area: block-start span-inline-start; +} +:host([positioning='below-start']) { + --position-area: block-end span-inline-end; +} +:host([positioning='below']) { + --position-area: block-end; +} +:host([positioning='below-end']) { + --position-area: block-end span-inline-start; +} +:host([positioning='before-top']) { + --position-area: inline-start span-block-end; +} +:host([positioning='before']) { + --position-area: inline-start; +} +:host([positioning='before-bottom']) { + --position-area: inline-start span-block-start; +} +:host([positioning='after-top']) { + --position-area: inline-end span-block-end; +} +:host([positioning='after']) { + --position-area: inline-end; +} +:host([positioning='after-bottom']) { + --position-area: inline-end span-block-start; +} diff --git a/packages/web-components/src/tooltip/tooltip.template.html b/packages/web-components/src/tooltip/tooltip.template.html new file mode 100644 index 0000000000000..81399e9401f64 --- /dev/null +++ b/packages/web-components/src/tooltip/tooltip.template.html @@ -0,0 +1,6 @@ + + + diff --git a/packages/web-components/src/tree-item/tree-item.styles.css b/packages/web-components/src/tree-item/tree-item.styles.css new file mode 100644 index 0000000000000..e1889226efb1d --- /dev/null +++ b/packages/web-components/src/tree-item/tree-item.styles.css @@ -0,0 +1,161 @@ +:host([hidden]) { + display: none; +} +:host { + display: block; +} + +:host { + outline: none; + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); +} + +:host(:focus-visible) .positioning-region { + box-shadow: var(--spacingVerticalNone) var(--spacingVerticalNone) var(--spacingVerticalNone) var(--spacingVerticalXXS) + var(--colorStrokeFocus2) inset; +} + +/** + * Default variants: + * Size - medium + * Appearance - subtle + */ +.positioning-region { + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + height: var(--spacingVerticalXXXL); + padding-inline-start: calc(var(--indent) * var(--spacingHorizontalXXL)); + padding-inline-end: var(--spacingVerticalS); + border-radius: var(--borderRadiusMedium); + background-color: var(--colorSubtleBackground); + color: var(--colorNeutralForeground2); + gap: var(--spacingHorizontalXS); +} + +@media (prefers-contrast: more) { + :host(:focus-visible) .positioning-region { + outline: 1px solid var(--colorStrokeFocus2); + } +} + +.content { + display: flex; + align-items: center; + gap: var(--spacingHorizontalXS); +} + +.chevron { + display: flex; + align-items: center; + flex-shrink: 0; + justify-content: center; + width: var(--spacingHorizontalXXL); + height: var(--spacingVerticalXXL); + transition: transform var(--durationFaster) var(--curveEasyEaseMax); + transform: rotate(0deg); +} + +.chevron:dir(rtl) { + transform: rotate(180deg); +} + +.chevron svg { + inline-size: 12px; + block-size: 12px; +} + +.aside { + display: flex; + align-items: center; +} + +.positioning-region:hover { + background-color: var(--colorSubtleBackgroundHover); + color: var(--colorNeutralForeground2Hover); +} + +.positioning-region:active { + background-color: var(--colorSubtleBackgroundPressed); + color: var(--colorNeutralForeground2Pressed); +} + +::slotted([slot='start']), +::slotted([slot='end']), +::slotted(:not([slot])) { + display: flex; + align-items: center; + min-width: 0; +} + +::slotted([slot='start']) { + flex-shrink: 0; +} + +::slotted(:not([slot])) { + padding-inline: var(--spacingHorizontalXXS); +} + +.items { + display: none; +} + +:host([expanded]) .items { + display: block; +} + +:host([empty]) .chevron, +:host([empty]) .items { + visibility: hidden; +} + +:host([selected]) .positioning-region { + background-color: var(--colorSubtleBackgroundSelected); + color: var(--colorNeutralForeground2Selected); +} + +:host([selected]) .content, +:host([selected]) .chevron { + color: var(--colorNeutralForeground3Selected); +} + +:host([size='small']) .positioning-region { + height: var(--spacingVerticalXXL); + padding-inline-start: calc(var(--indent) * var(--spacingHorizontalM)); +} + +:host([appearance='subtle-alpha']) .positioning-region:hover { + background-color: var(--colorSubtleBackgroundLightAlphaHover); +} + +:host([appearance='subtle-alpha']) .positioning-region:active { + background-color: var(--colorSubtleBackgroundLightAlphaPressed); +} + +:host([appearance='subtle-alpha'][selected]) .positioning-region { + background-color: var(--colorSubtleBackgroundLightAlphaSelected); + color: var(--colorNeutralForeground2Selected); +} + +:host([appearance='transparent']) .positioning-region { + background-color: var(--colorTransparentBackground); +} + +:host([appearance='transparent']) .positioning-region:hover { + background-color: var(--colorTransparentBackgroundHover); +} + +:host([appearance='transparent']) .positioning-region:active { + background-color: var(--colorTransparentBackgroundPressed); +} + +:host([appearance='transparent'][selected]) .positioning-region { + background-color: var(--colorTransparentBackgroundSelected); + color: var(--colorNeutralForeground2Selected); +} + +:host([expanded]) .chevron { + transform: rotate(90deg); +} diff --git a/packages/web-components/src/tree-item/tree-item.template.html b/packages/web-components/src/tree-item/tree-item.template.html new file mode 100644 index 0000000000000..6e07aedefba33 --- /dev/null +++ b/packages/web-components/src/tree-item/tree-item.template.html @@ -0,0 +1,27 @@ + + + diff --git a/packages/web-components/src/tree/tree.styles.css b/packages/web-components/src/tree/tree.styles.css new file mode 100644 index 0000000000000..4369a99911f55 --- /dev/null +++ b/packages/web-components/src/tree/tree.styles.css @@ -0,0 +1,10 @@ +:host([hidden]) { + display: none; +} +:host { + display: block; +} + +:host { + outline: none; +} diff --git a/packages/web-components/src/tree/tree.template.html b/packages/web-components/src/tree/tree.template.html new file mode 100644 index 0000000000000..450b538fc68fe --- /dev/null +++ b/packages/web-components/src/tree/tree.template.html @@ -0,0 +1,13 @@ + + + From 528c4ccfa7d913dafc674d84e8d80a3822f9f47e Mon Sep 17 00:00:00 2001 From: John Kreitlow <863023+radium-v@users.noreply.github.com> Date: Wed, 27 May 2026 23:37:21 -0700 Subject: [PATCH 4/7] add changefile --- ...eb-components-39809c10-c214-4e43-b550-a3f303e597d0.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-web-components-39809c10-c214-4e43-b550-a3f303e597d0.json diff --git a/change/@fluentui-web-components-39809c10-c214-4e43-b550-a3f303e597d0.json b/change/@fluentui-web-components-39809c10-c214-4e43-b550-a3f303e597d0.json new file mode 100644 index 0000000000000..9d833897e1290 --- /dev/null +++ b/change/@fluentui-web-components-39809c10-c214-4e43-b550-a3f303e597d0.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "feat(web-components): generate SSR templates and stylesheets into src/ and copy into dist during compile", + "packageName": "@fluentui/web-components", + "email": "863023+radium-v@users.noreply.github.com", + "dependentChangeType": "patch" +} From b080c62c3dbbf47c5921d27da5a31b2b013b91c3 Mon Sep 17 00:00:00 2001 From: John Kreitlow <863023+radium-v@users.noreply.github.com> Date: Wed, 27 May 2026 23:50:17 -0700 Subject: [PATCH 5/7] update readme with dsd generation details --- packages/web-components/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/web-components/README.md b/packages/web-components/README.md index 5945f3d08569a..51f206621085c 100644 --- a/packages/web-components/README.md +++ b/packages/web-components/README.md @@ -88,6 +88,24 @@ import CEM from '@fluentui/custom-elements.json' with { type: 'json' }; To start the component development environment, run `yarn start`. +### SSR templates and stylesheets + +Each component ships a declarative-shadow-DOM template (`*.template.html`) and an extracted stylesheet (`*.styles.css`) next to its `*.template.ts` and `*.styles.ts` sources. These files are generated from the TypeScript sources and committed to the repo so the DSD output is visible without running a build. + +After editing a `*.template.ts` or `*.styles.ts`, regenerate the matching HTML and CSS with: + +```sh +yarn generate:ssr +``` + +To check that the committed files match what the generators would produce (for example, before opening a PR), run: + +```sh +yarn check:ssr +``` + +`yarn compile` does not regenerate these files; it copies them from `src/` into `dist/esm/` alongside the compiled JS. + ### Known issue with Storybook site hot-reloading during development Storybook will watch modules for changes and hot-reload the module when necessary. This is usually great but poses a problem when the module being hot-reloaded defines a custom element. A custom element name can only be defined by the `CustomElementsRegistry` once, so reloading a module that defines a custom element will attempt to re-register the custom element name, throwing an error because the name has already been defined. This error will manifest with the following message: From 96950cbef4a2137b62611819e7020a6f6b72fa91 Mon Sep 17 00:00:00 2001 From: John Kreitlow <863023+radium-v@users.noreply.github.com> Date: Tue, 9 Jun 2026 12:30:08 -0700 Subject: [PATCH 6/7] chore: update dependencies for fast-build, fast-html, and fast-test-harness --- package.json | 6 +++--- yarn.lock | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 9c279de29feb4..37b3c5891bccc 100644 --- a/package.json +++ b/package.json @@ -72,10 +72,10 @@ "@microsoft/api-extractor": "7.51.0", "@microsoft/api-extractor-model": "7.31.2", "@microsoft/eslint-plugin-sdl": "1.0.1", - "@microsoft/fast-build": "0.6.0", + "@microsoft/fast-build": "0.7.0", "@microsoft/fast-element": "2.10.4", - "@microsoft/fast-html": "1.0.0-alpha.53", - "@microsoft/fast-test-harness": "0.3.0", + "@microsoft/fast-html": "1.0.0-alpha.54", + "@microsoft/fast-test-harness": "0.3.1", "@microsoft/focusgroup-polyfill": "1.5.0", "@microsoft/load-themed-styles": "1.10.26", "@microsoft/loader-load-themed-styles": "2.0.17", diff --git a/yarn.lock b/yarn.lock index 9774592d8cc4e..f17c7de2c74f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2747,25 +2747,25 @@ eslint-plugin-react "7.35.2" eslint-plugin-security "1.4.0" -"@microsoft/fast-build@0.6.0": - version "0.6.0" - resolved "https://registry.yarnpkg.com/@microsoft/fast-build/-/fast-build-0.6.0.tgz#9791d6100d68ba7104a7727db44cf4fc27a6c939" - integrity sha512-N5sApsoDETr4/iHyr99iy9lfAxHsNFIzyEezGmQyWa06rzXn8QK+pLF7EKtp0N4VUsB8i+s62dtIsCgz8kp/2g== +"@microsoft/fast-build@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@microsoft/fast-build/-/fast-build-0.7.0.tgz#a7167f8ae1bbd4a1ae379ac3b7bf84d68f464dd7" + integrity sha512-YKqjlwFwurd69dL6YWbvGdJ7TX8c2h4Ra7qUGUMTSQAhrnREXjtB6/cduPnkswxBfDOMwYBeaVJCDcMP5auLZg== "@microsoft/fast-element@2.10.4", "@microsoft/fast-element@^2.0.0": version "2.10.4" resolved "https://registry.yarnpkg.com/@microsoft/fast-element/-/fast-element-2.10.4.tgz#12b8c6c90902f2a54d5b27f618d7d095e133546d" integrity sha512-OkHIlMztq7+PgkRF1LscgBd/MVzMhGrWPkJRDvOEqdqEdZOKocKvaKcUKZubIBz4RpLIrhLD3lOJzCsspk6jXg== -"@microsoft/fast-html@1.0.0-alpha.53": - version "1.0.0-alpha.53" - resolved "https://registry.yarnpkg.com/@microsoft/fast-html/-/fast-html-1.0.0-alpha.53.tgz#e656a38386a998640fd7af40bbcd5aae321092f9" - integrity sha512-S6fUlNjk3aj9SP1U4sRWA1Cf1gns7FLJOed3NMvndWQBfmQczVX8CHXbfVKF55B7nKWRkAQwDHN0wOu7yxw+aw== +"@microsoft/fast-html@1.0.0-alpha.54": + version "1.0.0-alpha.54" + resolved "https://registry.yarnpkg.com/@microsoft/fast-html/-/fast-html-1.0.0-alpha.54.tgz#66c670aad1dd2600941029d188ed63773baaed20" + integrity sha512-7GWGwaJSWD/nqafn/s1YTqc5CmHbRWwEC1BA/BJHSLrX+UQZGGbCugSQ0ArL4x7goDmB6rWLTPB7UZtqOcu/3g== -"@microsoft/fast-test-harness@0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@microsoft/fast-test-harness/-/fast-test-harness-0.3.0.tgz#9d5536c8cbf1e4ecae9d70bfe0fe105b5e215caf" - integrity sha512-EZ0iLxy/AEaOMthJmalD4fIp4rAM/ypBfqXmMJD4ZffVUVnLkfUw2NFZNUIsMbP/S4+tsfBnBlX5tyKSYD5TIQ== +"@microsoft/fast-test-harness@0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@microsoft/fast-test-harness/-/fast-test-harness-0.3.1.tgz#92c671ab9d76751b41a5dc447ec1fe74420796aa" + integrity sha512-BxwSSJhRbCJkyjJv9xsOl0ECAHjtU+zZk4PDse7jy/iqEb2wf51QLVrHZQAgWPZWAI6OlMTrjdZTRfiea3ciWA== dependencies: cheerio "1.2.0" From a3520415b37be764464da7283d94f12ad8201f4e Mon Sep 17 00:00:00 2001 From: John Kreitlow <863023+radium-v@users.noreply.github.com> Date: Tue, 9 Jun 2026 12:32:58 -0700 Subject: [PATCH 7/7] update stylesheets --- .../src/dialog/dialog.styles.css | 1 - .../src/dropdown/dropdown.styles.css | 1 - .../src/listbox/listbox.styles.css | 24 +++++++------------ .../src/menu-item/menu-item.styles.css | 10 ++++---- .../web-components/src/menu/menu.styles.css | 13 +++++----- .../src/tablist/tablist.styles.css | 2 +- .../src/tooltip/tooltip.styles.css | 2 +- 7 files changed, 21 insertions(+), 32 deletions(-) diff --git a/packages/web-components/src/dialog/dialog.styles.css b/packages/web-components/src/dialog/dialog.styles.css index 39f6e276c1508..3e9d9d8bd9f0d 100644 --- a/packages/web-components/src/dialog/dialog.styles.css +++ b/packages/web-components/src/dialog/dialog.styles.css @@ -22,7 +22,6 @@ :host([type='non-modal']) dialog { inset: 0; - position: fixed; z-index: 2; overflow: auto; } diff --git a/packages/web-components/src/dropdown/dropdown.styles.css b/packages/web-components/src/dropdown/dropdown.styles.css index 4786eacf19905..12bd0156cac30 100644 --- a/packages/web-components/src/dropdown/dropdown.styles.css +++ b/packages/web-components/src/dropdown/dropdown.styles.css @@ -6,7 +6,6 @@ } :host { - anchor-name: --dropdown-trigger; box-sizing: border-box; color: var(--colorNeutralForeground1); cursor: pointer; diff --git a/packages/web-components/src/listbox/listbox.styles.css b/packages/web-components/src/listbox/listbox.styles.css index c9acaeb45ec7e..6d5232349532a 100644 --- a/packages/web-components/src/listbox/listbox.styles.css +++ b/packages/web-components/src/listbox/listbox.styles.css @@ -13,7 +13,7 @@ box-sizing: border-box; flex-direction: column; margin: 0; - min-width: 160px; + min-inline-size: 160px; padding: var(--spacingHorizontalXS); row-gap: var(--spacingHorizontalXXS); width: auto; @@ -26,26 +26,20 @@ @supports (anchor-name: --anchor) { :host([popover]) { - position: absolute; - margin-block-start: 0; - max-height: var(--listbox-max-height, calc(50vh - anchor-size(self-block))); - min-width: anchor-size(width); - position-anchor: --dropdown; - position-area: block-end span-inline-end; - position-try-fallbacks: flip-inline, flip-block, --flip-block, block-start; - } - - @position-try --flip-block { - bottom: anchor(top); - top: unset; + position: fixed; + max-block-size: var(--listbox-max-height, calc(50vh - anchor-size(self-block))); + min-inline-size: anchor-size(inline); + inset-block-start: anchor(end); + inset-inline-start: anchor(start); + position-try-fallbacks: flip-block, flip-inline, flip-inline flip-block; } } @supports not (anchor-name: --anchor) { :host([popover]) { margin-block-start: var(--margin-offset, 0); - max-height: var(--listbox-max-height, 50vh); - position: fixed; + max-block-size: var(--listbox-max-height, 50vh); + position: absolute; } :host([popover]:state(flip-block)) { diff --git a/packages/web-components/src/menu-item/menu-item.styles.css b/packages/web-components/src/menu-item/menu-item.styles.css index b0935e3646bf3..66e0ccb972125 100644 --- a/packages/web-components/src/menu-item/menu-item.styles.css +++ b/packages/web-components/src/menu-item/menu-item.styles.css @@ -124,9 +124,11 @@ ::slotted([popover]) { margin: 0; max-height: var(--menu-max-height, auto); - position: absolute; + position: fixed; position-anchor: --menu-trigger; - position-area: inline-end span-block-end; + inset: unset; + inset-block-start: anchor(start); + inset-inline-start: anchor(end); position-try-fallbacks: flip-inline, block-start, block-end; z-index: 1; } @@ -135,10 +137,6 @@ display: none; } - ::slotted([popover]:popover-open) { - inset: unset; - } - /* Fallback for no anchor-positioning */ @supports not (anchor-name: --menu-trigger) { ::slotted([popover]) { diff --git a/packages/web-components/src/menu/menu.styles.css b/packages/web-components/src/menu/menu.styles.css index 6b45d94042a02..f8c38f7465296 100644 --- a/packages/web-components/src/menu/menu.styles.css +++ b/packages/web-components/src/menu/menu.styles.css @@ -13,18 +13,17 @@ margin: 0; max-height: var(--menu-max-height, auto); position-anchor: --menu-trigger; - position-area: block-end span-inline-end; + inset: unset; + inset-block-start: anchor(end); + inset-inline-start: anchor(start); position-try-fallbacks: flip-block; - position: absolute; + position: fixed; z-index: 1; } :host([split]) ::slotted([popover]) { - position-area: block-end span-inline-start; -} - -::slotted([popover]:popover-open) { - inset: unset; + inset-inline-start: auto; + inset-inline-end: anchor(end); } ::slotted([popover]:not(:popover-open)) { diff --git a/packages/web-components/src/tablist/tablist.styles.css b/packages/web-components/src/tablist/tablist.styles.css index ad2a25c1f36a3..71072de498804 100644 --- a/packages/web-components/src/tablist/tablist.styles.css +++ b/packages/web-components/src/tablist/tablist.styles.css @@ -173,7 +173,7 @@ content: ''; inline-size: 100%; inset: auto auto anchor(end) anchor(center); - position: absolute; + position: fixed; position-anchor: --tab; transform: translateX(-50%); transition-property: inset-inline, width; diff --git a/packages/web-components/src/tooltip/tooltip.styles.css b/packages/web-components/src/tooltip/tooltip.styles.css index 8f575709f5879..ecf0ff7b9a69f 100644 --- a/packages/web-components/src/tooltip/tooltip.styles.css +++ b/packages/web-components/src/tooltip/tooltip.styles.css @@ -31,7 +31,7 @@ padding: 4px var(--spacingHorizontalMNudge) 6px; position: absolute; position-area: var(--position-area); - position-try-options: var(--position-try-options); + position-try-fallbacks: var(--position-try-options); width: auto; z-index: 1; }