diff --git a/cspell.json b/cspell.json
index e5b28277e..6c381b4df 100644
--- a/cspell.json
+++ b/cspell.json
@@ -105,6 +105,7 @@
"keydown",
"keyframes",
"keymaker",
+ "Kickstarter",
"Konnor",
"Kool",
"labelledby",
@@ -117,6 +118,7 @@
"lowercasing",
"Lucide",
"maxlength",
+ "mdash",
"Menlo",
"menuitemcheckbox",
"menuitemradio",
@@ -130,6 +132,7 @@
"mouseout",
"mouseup",
"multiselectable",
+ "nbsp",
"nextjs",
"nocheck",
"noindex",
@@ -179,6 +182,7 @@
"shadowrootmode",
"Shortcode",
"Shortcodes",
+ "signup",
"sitedir",
"slotchange",
"smartquotes",
diff --git a/package-lock.json b/package-lock.json
index f82819c0d..dd58b9bd2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14029,7 +14029,8 @@
"devDependencies": {
"@wc-toolkit/jsx-types": "^1.3.0",
"eleventy-plugin-git-commit-date": "^0.1.3",
- "esbuild": "^0.25.11"
+ "esbuild": "^0.25.11",
+ "npm-check-updates": "^19.1.2"
},
"engines": {
"node": ">=14.17.0"
@@ -14037,7 +14038,7 @@
},
"packages/webawesome-pro": {
"name": "@shoelace-style/webawesome-pro",
- "version": "3.0.0-beta.6",
+ "version": "3.0.0",
"dependencies": {
"@ctrl/tinycolor": "4.1.0",
"@floating-ui/dom": "^1.6.13",
@@ -14052,7 +14053,8 @@
"devDependencies": {
"@wc-toolkit/jsx-types": "^1.3.0",
"eleventy-plugin-git-commit-date": "^0.1.3",
- "esbuild": "^0.25.11"
+ "esbuild": "^0.25.11",
+ "npm-check-updates": "^19.1.2"
},
"engines": {
"node": ">=14.17.0"
@@ -14525,6 +14527,21 @@
"node": "^18 || >=20"
}
},
+ "packages/webawesome-pro/node_modules/npm-check-updates": {
+ "version": "19.1.2",
+ "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-19.1.2.tgz",
+ "integrity": "sha512-FNeFCVgPOj0fz89hOpGtxP2rnnRHR7hD2E8qNU8SMWfkyDZXA/xpgjsL3UMLSo3F/K13QvJDnbxPngulNDDo/g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "ncu": "build/cli.js",
+ "npm-check-updates": "build/cli.js"
+ },
+ "engines": {
+ "node": ">=20.0.0",
+ "npm": ">=8.12.1"
+ }
+ },
"packages/webawesome/node_modules/@esbuild/aix-ppc64": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz",
@@ -14966,6 +14983,20 @@
"engines": {
"node": "^18 || >=20"
}
+ },
+ "packages/webawesome/node_modules/npm-check-updates": {
+ "version": "19.1.2",
+ "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-19.1.2.tgz",
+ "integrity": "sha512-FNeFCVgPOj0fz89hOpGtxP2rnnRHR7hD2E8qNU8SMWfkyDZXA/xpgjsL3UMLSo3F/K13QvJDnbxPngulNDDo/g==",
+ "dev": true,
+ "bin": {
+ "ncu": "build/cli.js",
+ "npm-check-updates": "build/cli.js"
+ },
+ "engines": {
+ "node": ">=20.0.0",
+ "npm": ">=8.12.1"
+ }
}
}
}
diff --git a/package.json b/package.json
index 3393e2f48..2f2b446cc 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "@webawesome/monorepo",
"private": true,
"description": "A forward-thinking library of web components.",
- "version": "3.0.0-alpha.13",
+ "version": "3.0.0",
"homepage": "https://webawesome.com/",
"author": "Web Awesome",
"license": "MIT",
@@ -85,4 +85,4 @@
"prettier --write"
]
}
-}
+}
\ No newline at end of file
diff --git a/packages/webawesome/docs/_includes/_dialog-wa-launch.njk b/packages/webawesome/docs/_includes/_dialog-wa-launch.njk
index e1ddbeb19..e1326ccbc 100644
--- a/packages/webawesome/docs/_includes/_dialog-wa-launch.njk
+++ b/packages/webawesome/docs/_includes/_dialog-wa-launch.njk
@@ -14,8 +14,8 @@
Celebrate our official launch with a 20% discount on a Web Awesome Pro plan…for life ! But hurry, this lifetime discount is only available for a limited time.
-
Maybe Later
-
+ Maybe Later
+
Get Pro + Save 20%
@@ -43,13 +43,6 @@
return;
}
- // Helper function to safely track Plausible events
- const trackEvent = (eventName) => {
- if (typeof plausible !== 'undefined') {
- plausible(eventName);
- }
- };
-
// Initialize dialog functionality
let initCalled = false;
const initDialog = () => {
@@ -59,19 +52,8 @@
}
initCalled = true;
- // Track when dialog is shown
- dialog.addEventListener('wa-show', () => {
- trackEvent('launch_dialog:view');
- }, { once: true });
-
- // Track when dialog is dismissed
+ // Save dismissal state when dialog is hidden
dialog.addEventListener('wa-hide', (event) => {
- // Track overlay click or Escape key dismissal
- // Button clicks are tracked via CSS classes, so we only track non-button dismissals
- if (event.detail?.source === dialog) {
- trackEvent('launch_dialog:overlay_click');
- }
-
// Save dismissal state to localStorage
try {
localStorage.setItem(SITE_DIALOG_DISMISSED_KEY, 'true');
diff --git a/packages/webawesome/docs/_includes/base.njk b/packages/webawesome/docs/_includes/base.njk
index 26b50c2b1..eb7d837ec 100644
--- a/packages/webawesome/docs/_includes/base.njk
+++ b/packages/webawesome/docs/_includes/base.njk
@@ -17,7 +17,6 @@
{% if hasSidebar %}{% endif %}
-
{% block head %}
@@ -142,6 +141,9 @@
{% include "_dialog-wa-launch.njk" ignore missing %}
{% endif %}
+ {#- Cookie Consent Dialog -#}
+ {% include "cookie-consent.njk" ignore missing %}
+
{# Footer #}
{% block pageFooter %}{% endblock %}
diff --git a/packages/webawesome/docs/_includes/sidebar.njk b/packages/webawesome/docs/_includes/sidebar.njk
index ee902cd5d..1f85fb8c9 100644
--- a/packages/webawesome/docs/_includes/sidebar.njk
+++ b/packages/webawesome/docs/_includes/sidebar.njk
@@ -78,10 +78,18 @@
- Charts {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}
+ Charts {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }} {{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}
Checkbox
Color Picker
- Combobox {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}
+
+
+
+ Combobox
+
+
+ {{ proBadge() }}
+
+
Comparison
@@ -89,8 +97,8 @@
- Data Grid {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}
- Datepicker {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}
+ Data Grid {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }} {{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}
+ Date Picker {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }} {{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}
Details
Dialog
Divider
@@ -101,7 +109,7 @@
Dropdown Item
- File Input {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}
+ File Input {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }} {{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}
Format Bytes
Format Date
Format Number
@@ -146,7 +154,7 @@
Tag
Textarea
- Toast {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}
+ Toast {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }} {{ proBadge( { description: "This will require access to Web Awesome Pro" }) }}
Tooltip
Tree
@@ -154,7 +162,7 @@
Tree Item
- Video {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}
+ Video {{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }} {{ proBadge( { description: "This will require access to Web Awesome Pro" }) }}
Zoomable Frame
{# PLOP_NEW_COMPONENT_PLACEHOLDER #}
diff --git a/packages/webawesome/docs/_layouts/component.njk b/packages/webawesome/docs/_layouts/component.njk
index 4c260f17c..b6dd8d18d 100644
--- a/packages/webawesome/docs/_layouts/component.njk
+++ b/packages/webawesome/docs/_layouts/component.njk
@@ -8,10 +8,13 @@
Since {{ component.since }}
{{ component.status }}
+ {% if isProComponent %}
+ Pro
+ {% endif %}
{{ component.summary | inlineMarkdown | safe }}
@@ -20,6 +23,37 @@
{# Component API #}
{% block afterContent %}
+ {# Importing #}
+
Importing
+
+ Autoloading components via projects is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
+
+
+ {% set componentName = component.tagName | stripPrefix %}
+ {% set componentPath = ["components/", componentName, "/", componentName, ".js"] | join("") %}
+
+ CDN
+ npm
+ React
+
+
+ Let your project code do the work! Sign up for free to use a project with your very own CDN — it's the fastest and easiest way to use Web Awesome.
+
+
+
+
+ To manually import this component from NPM, use the following code.
+
+ import '@awesome.me/webawesome/dist/{{ componentPath }}';
+
+
+
+ To manually import this component from React, use the following code.
+
+ import {{ component.name }} from '@awesome.me/webawesome/dist/react/{{ componentName }}';
+
+
+
{# Slots #}
{% if component.slots.length %}
Slots
@@ -270,38 +304,6 @@
{% endif %}
- {# Importing #}
- Importing
-
- Autoloading components via projects is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
-
-
-
- {% set componentName = component.tagName | stripPrefix %}
- {% set componentPath = ["components/", componentName, "/", componentName, ".js"] | join("") %}
-
- CDN
- npm
- React
-
-
- Let your project code do the work! Sign up for free to use a project with your very own CDN — it's the fastest and easiest way to use Web Awesome.
-
-
-
-
- To manually import this component from NPM, use the following code.
-
- import '@awesome.me/webawesome/dist/{{ componentPath }}';
-
-
-
- To manually import this component from React, use the following code.
-
- import {{ component.name }} from '@awesome.me/webawesome/dist/react/{{ componentName }}';
-
-
-
diff --git a/packages/webawesome/docs/_utils/simulate-webawesome-app.js b/packages/webawesome/docs/_utils/simulate-webawesome-app.js
index 0c07a25c7..f24682521 100644
--- a/packages/webawesome/docs/_utils/simulate-webawesome-app.js
+++ b/packages/webawesome/docs/_utils/simulate-webawesome-app.js
@@ -1,10 +1,20 @@
+import * as path from 'node:path';
import nunjucks from 'nunjucks';
+const baseDir = process.env.BASE_DIR || 'docs';
+
+const views = [path.join(baseDir), path.join(baseDir, '_layouts'), path.join(baseDir, '_includes')];
+
+const nunjucksEnv = new nunjucks.Environment(new nunjucks.FileSystemLoader(views), {
+ autoescape: true,
+ noCache: process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test',
+});
+
/**
* This function simulates what a server would do running "on top" of eleventy.
*/
export function SimulateWebAwesomeApp(str) {
- return nunjucks.renderString(str, {
+ return nunjucksEnv.renderString(str, {
// Stub the server EJS shortcodes.
currentUser: {
hasPro: false,
diff --git a/packages/webawesome/docs/docs/components/button-group.md b/packages/webawesome/docs/docs/components/button-group.md
index ce4c5ebf4..5a95cd281 100644
--- a/packages/webawesome/docs/docs/components/button-group.md
+++ b/packages/webawesome/docs/docs/components/button-group.md
@@ -40,60 +40,6 @@ Set the `orientation` attribute to `vertical` to make a vertical button group.
```
-### Theme Buttons
-
-Theme buttons are supported through the button group's `variant` attribute.
-
-```html {.example}
-
- Left
- Center
- Right
-
-
-
-
-
- Left
- Center
- Right
-
-
-
-
-
- Left
- Center
- Right
-
-
-
-
-
- Left
- Center
- Right
-
-
-
-
-
- Left
- Center
- Right
-
-```
-
-You can still use the buttons’ own `variant` attribute to override the inherited variant.
-
-```html {.example}
-
- Left
- Center
- Right
-
-```
-
### Pill Buttons
Pill buttons are supported through the button's `pill` attribute.
diff --git a/packages/webawesome/docs/docs/components/intersection-observer.md b/packages/webawesome/docs/docs/components/intersection-observer.md
index 1252b2bcd..c2fd70e25 100644
--- a/packages/webawesome/docs/docs/components/intersection-observer.md
+++ b/packages/webawesome/docs/docs/components/intersection-observer.md
@@ -2,6 +2,7 @@
title: Intersection Observer
description: Tracks immediate child elements and fires events as they move in and out of view.
layout: component
+category: Utilities
---
This component leverages the [IntersectionObserver API](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) to track when its direct children enter or leave a designated root element. The `wa-intersect` event fires whenever elements cross the visibility threshold.
diff --git a/packages/webawesome/docs/docs/components/select.md b/packages/webawesome/docs/docs/components/select.md
index ff7135e8a..2878fc00b 100644
--- a/packages/webawesome/docs/docs/components/select.md
+++ b/packages/webawesome/docs/docs/components/select.md
@@ -285,9 +285,10 @@ Remember that custom tags are rendered in a shadow root. To style them, you can
const name = option.querySelector('wa-icon[slot="start"]').name;
// You can return a string, a Lit Template, or an HTMLElement here
+ // Important: include data-value so the tag can be removed properly!
return `
-
-
+
+
${option.label}
`;
@@ -299,6 +300,10 @@ Remember that custom tags are rendered in a shadow root. To style them, you can
Be sure you trust the content you are outputting! Passing unsanitized user input to `getTag()` can result in XSS vulnerabilities.
:::
+:::info
+When using custom tags with `with-remove`, you must include the `data-value` attribute set to the option's value. This allows the select to identify which option to deselect when the tag's remove button is clicked.
+:::
+
### Lazy loading options
Lazy loading options works similarly to native `` elements. The select component handles various scenarios intelligently:
diff --git a/packages/webawesome/docs/docs/resources/changelog.md b/packages/webawesome/docs/docs/resources/changelog.md
index a6f2c5e36..ae2a2a9d1 100644
--- a/packages/webawesome/docs/docs/resources/changelog.md
+++ b/packages/webawesome/docs/docs/resources/changelog.md
@@ -13,6 +13,9 @@ Components with the Experimental badge sh
## Next
+- Added `` as an experimental pro component [issue:1074]
+- Added version 2.0.0 of the [official Web Awesome Figma Design Kit](/docs/resources/figma)
+- Added npm support for Web Awesome Pro
- Added `layers.css` to define cascade layer order and updated palettes, themes, native styles, and utilities to import the new rule for more fail-safe modularity [pr:1793]
- Fixed a bug in `` that caused some touch devices to end up with the incorrect value [issue:1703]
- Fixed a bug in `` that prevented some slots from being detected correctly [discuss:1450]
@@ -20,7 +23,14 @@ Components with the Experimental badge sh
- Fixed a bug in `` that caused some icon libraries to render with the incorrect SVG fill [issue:1733]
- Fixed a bug in `` that caused the spinner to not show when lazy loading [issue:1678]
- Fixed a bug in `` that caused the browser to hang when cancelling the `wa-hide` event [issue:1483]
+- Fixed a bug in `` that prevented the icon dependency from being imported [issue:1825]
+- Fixed a bug in `` that prevented clicks on the tag's remove button from removing options in multiple mode
+- Fixed a bug in `` that caused tags to appear in alphabetical order instead of selection order when using `multiple`
- Improved performance of `` so initial rendering occurs faster, especially with multiple icons on the page [issue:1729]
+- Improved `` to not throw an error when string values are passed to the `min`, `max`, and `step` properties [issue:1823]
+- Fixed a bug in Web Awesome form controls that caused `` to set the form property to equal `"foo"` instead of returning an `HTMLFormElement` breaking platform expectations. [pr:1815]
+- Fixed a bug in `` causing it to not copy over attributes for form submissions. [pr:1815]
+- Improved performance of all components by fixing how CSS is imported and reused [issue:1812]
- Modified the default `transition` styles of `` to use design tokens [pr:1693]
## 3.0.0
@@ -505,4 +515,4 @@ Many of these changes and improvements were the direct result of feedback from u
-Did we miss something? [Let us know!](https://github.com/shoelace-style/webawesome/discussions)
+Did we miss something? [Let us know!](https://github.com/shoelace-style/webawesome/discussions)
\ No newline at end of file
diff --git a/packages/webawesome/package.json b/packages/webawesome/package.json
index 0ed715c79..6cac74d54 100644
--- a/packages/webawesome/package.json
+++ b/packages/webawesome/package.json
@@ -68,10 +68,10 @@
"spellcheck": "cspell \"**/*.{js,ts,json,html,css,md}\" --no-progress --config=\"../../cspell.json\"",
"verify": "npm run prettier && npm run build && npm run test",
"prepublishOnly": "npm run verify",
- "check-updates": "npx npm-check-updates --interactive --format group",
+ "check-updates": "npm-check-updates --cooldown 7 --interactive --format group",
"print-version": "echo $npm_package_version",
"tag-version": "git tag -a \"v$(npm run print-version | tail -n1)\" -m \"tag v$(npm run print-version | tail -n1)\"",
- "postversion": "npm run tag-version"
+ "postversion": "npm run tag-version && node ./scripts/update-root-version.js"
},
"engines": {
"node": ">=14.17.0"
@@ -95,6 +95,7 @@
"devDependencies": {
"@wc-toolkit/jsx-types": "^1.3.0",
"eleventy-plugin-git-commit-date": "^0.1.3",
- "esbuild": "^0.25.11"
+ "esbuild": "^0.25.11",
+ "npm-check-updates": "^19.1.2"
}
}
diff --git a/packages/webawesome/scripts/build.js b/packages/webawesome/scripts/build.js
index 232a8f2ce..46456d1d4 100644
--- a/packages/webawesome/scripts/build.js
+++ b/packages/webawesome/scripts/build.js
@@ -235,9 +235,6 @@ export async function build(options = {}) {
js: `/*! Copyright ${currentYear} Fonticons, Inc. - https://webawesome.com/license */`,
},
plugins: [replace({ __WEBAWESOME_VERSION__: await getVersion() })],
- loader: {
- '.css': 'text',
- },
};
const unbundledConfig = {
diff --git a/packages/webawesome/scripts/update-root-version.js b/packages/webawesome/scripts/update-root-version.js
new file mode 100755
index 000000000..20490af94
--- /dev/null
+++ b/packages/webawesome/scripts/update-root-version.js
@@ -0,0 +1,27 @@
+#!/usr/bin/env node
+
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import * as url from 'url';
+
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
+
+const monorepoRoot = path.resolve(__dirname, '..', '..', '..');
+const rootPackageJSONFile = path.join(monorepoRoot, 'package.json');
+const webawesomePackageJSONFile = path.join(path.resolve(__dirname, '..'), 'package.json');
+
+const rootPackageJSON = JSON.parse(fs.readFileSync(rootPackageJSONFile));
+const webawesomePackageJSON = JSON.parse(fs.readFileSync(webawesomePackageJSONFile));
+
+const currentVersion = webawesomePackageJSON.version;
+rootPackageJSON.version = currentVersion;
+
+fs.writeFileSync(rootPackageJSONFile, JSON.stringify(rootPackageJSON, null, 2));
+
+const versionsFile = path.join(monorepoRoot, 'VERSIONS.txt');
+const versions = fs.readFileSync(versions).split(/\r?\n/);
+
+// TODO: Make this smart and understand semver and "insert" in the correct spot instead of appending.
+if (!versions.includes(currentVersion)) {
+ fs.appendFileSync(webawesomePackageJSON.version);
+}
diff --git a/packages/webawesome/src/components/animated-image/animated-image.css b/packages/webawesome/src/components/animated-image/animated-image.css
deleted file mode 100644
index 59f659044..000000000
--- a/packages/webawesome/src/components/animated-image/animated-image.css
+++ /dev/null
@@ -1,65 +0,0 @@
-:host {
- --control-box-size: 3rem;
- --icon-size: calc(var(--control-box-size) * 0.625);
-
- display: inline-flex;
- position: relative;
- cursor: pointer;
-}
-
-img {
- display: block;
- width: 100%;
- height: 100%;
-}
-
-img[aria-hidden='true'] {
- display: none;
-}
-
-.control-box {
- display: flex;
- position: absolute;
- align-items: center;
- justify-content: center;
- top: calc(50% - var(--control-box-size) / 2);
- right: calc(50% - var(--control-box-size) / 2);
- width: var(--control-box-size);
- height: var(--control-box-size);
- font-size: calc(var(--icon-size) * 0.75);
- background: none;
- border: solid var(--wa-border-width-s) currentColor;
- background-color: rgb(0 0 0 / 50%);
- border-radius: var(--wa-border-radius-circle);
- color: white;
- pointer-events: none;
- transition: opacity var(--wa-transition-normal) var(--wa-transition-easing);
-}
-
-@media (hover: hover) {
- :host([play]:hover) .control-box {
- opacity: 1;
- }
-}
-
-:where(:host([play]:not(:hover))) .control-box {
- opacity: 0;
-}
-
-:host([play]) slot[name='play-icon'],
-:host(:not([play])) slot[name='pause-icon'] {
- display: none;
-}
-
-/* Show control box on keyboard focus */
-.animated-image {
- &:focus {
- outline: none;
- }
-
- &:focus-visible .control-box {
- opacity: 1;
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
- }
-}
diff --git a/packages/webawesome/src/components/animated-image/animated-image.styles.ts b/packages/webawesome/src/components/animated-image/animated-image.styles.ts
new file mode 100644
index 000000000..94675b814
--- /dev/null
+++ b/packages/webawesome/src/components/animated-image/animated-image.styles.ts
@@ -0,0 +1,69 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --control-box-size: 3rem;
+ --icon-size: calc(var(--control-box-size) * 0.625);
+
+ display: inline-flex;
+ position: relative;
+ cursor: pointer;
+ }
+
+ img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ }
+
+ img[aria-hidden='true'] {
+ display: none;
+ }
+
+ .control-box {
+ display: flex;
+ position: absolute;
+ align-items: center;
+ justify-content: center;
+ top: calc(50% - var(--control-box-size) / 2);
+ right: calc(50% - var(--control-box-size) / 2);
+ width: var(--control-box-size);
+ height: var(--control-box-size);
+ font-size: calc(var(--icon-size) * 0.75);
+ background: none;
+ border: solid var(--wa-border-width-s) currentColor;
+ background-color: rgb(0 0 0 / 50%);
+ border-radius: var(--wa-border-radius-circle);
+ color: white;
+ pointer-events: none;
+ transition: opacity var(--wa-transition-normal) var(--wa-transition-easing);
+ }
+
+ @media (hover: hover) {
+ :host([play]:hover) .control-box {
+ opacity: 1;
+ }
+ }
+
+ :where(:host([play]:not(:hover))) .control-box {
+ opacity: 0;
+ }
+
+ :host([play]) slot[name='play-icon'],
+ :host(:not([play])) slot[name='pause-icon'] {
+ display: none;
+ }
+
+ /* Show control box on keyboard focus */
+ .animated-image {
+ &:focus {
+ outline: none;
+ }
+
+ &:focus-visible .control-box {
+ opacity: 1;
+ outline: var(--wa-focus-ring);
+ outline-offset: var(--wa-focus-ring-offset);
+ }
+ }
+`;
diff --git a/packages/webawesome/src/components/animated-image/animated-image.ts b/packages/webawesome/src/components/animated-image/animated-image.ts
index f12ad4f06..c041841fa 100644
--- a/packages/webawesome/src/components/animated-image/animated-image.ts
+++ b/packages/webawesome/src/components/animated-image/animated-image.ts
@@ -6,7 +6,7 @@ import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
import '../icon/icon.js';
-import styles from './animated-image.css';
+import styles from './animated-image.styles.js';
/**
* @summary A component for displaying animated GIFs and WEBPs that play and pause on interaction.
diff --git a/packages/webawesome/src/components/animation/animation.css b/packages/webawesome/src/components/animation/animation.css
deleted file mode 100644
index 92d692cdd..000000000
--- a/packages/webawesome/src/components/animation/animation.css
+++ /dev/null
@@ -1,3 +0,0 @@
-:host {
- display: contents;
-}
diff --git a/packages/webawesome/src/components/animation/animation.styles.ts b/packages/webawesome/src/components/animation/animation.styles.ts
new file mode 100644
index 000000000..1ef4bf6f3
--- /dev/null
+++ b/packages/webawesome/src/components/animation/animation.styles.ts
@@ -0,0 +1,7 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ display: contents;
+ }
+`;
diff --git a/packages/webawesome/src/components/animation/animation.ts b/packages/webawesome/src/components/animation/animation.ts
index e77ee92aa..7a0e9b06d 100644
--- a/packages/webawesome/src/components/animation/animation.ts
+++ b/packages/webawesome/src/components/animation/animation.ts
@@ -5,7 +5,7 @@ import { WaFinishEvent } from '../../events/finish.js';
import { WaStartEvent } from '../../events/start.js';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import styles from './animation.css';
+import styles from './animation.styles.js';
import { animations } from './animations.js';
/**
diff --git a/packages/webawesome/src/components/avatar/avatar.css b/packages/webawesome/src/components/avatar/avatar.css
deleted file mode 100644
index 2c071cb5c..000000000
--- a/packages/webawesome/src/components/avatar/avatar.css
+++ /dev/null
@@ -1,53 +0,0 @@
-:host {
- --size: 3rem;
-
- display: inline-flex;
- align-items: center;
- justify-content: center;
- position: relative;
- width: var(--size);
- height: var(--size);
- color: var(--wa-color-neutral-on-normal);
- font: inherit;
- font-size: calc(var(--size) * 0.4);
- vertical-align: middle;
- background-color: var(--wa-color-neutral-fill-normal);
- border-radius: var(--wa-border-radius-circle);
- user-select: none;
- -webkit-user-select: none;
-}
-
-:host([shape='square']) {
- border-radius: 0;
-}
-
-:host([shape='rounded']) {
- border-radius: var(--wa-border-radius-m);
-}
-
-.icon {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-}
-
-.initials {
- line-height: 1;
- text-transform: uppercase;
-}
-
-.image {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- object-fit: cover;
- overflow: hidden;
- border-radius: inherit;
-}
diff --git a/packages/webawesome/src/components/avatar/avatar.styles.ts b/packages/webawesome/src/components/avatar/avatar.styles.ts
new file mode 100644
index 000000000..98d8c5ae4
--- /dev/null
+++ b/packages/webawesome/src/components/avatar/avatar.styles.ts
@@ -0,0 +1,57 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --size: 3rem;
+
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ width: var(--size);
+ height: var(--size);
+ color: var(--wa-color-neutral-on-normal);
+ font: inherit;
+ font-size: calc(var(--size) * 0.4);
+ vertical-align: middle;
+ background-color: var(--wa-color-neutral-fill-normal);
+ border-radius: var(--wa-border-radius-circle);
+ user-select: none;
+ -webkit-user-select: none;
+ }
+
+ :host([shape='square']) {
+ border-radius: 0;
+ }
+
+ :host([shape='rounded']) {
+ border-radius: var(--wa-border-radius-m);
+ }
+
+ .icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
+
+ .initials {
+ line-height: 1;
+ text-transform: uppercase;
+ }
+
+ .image {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ overflow: hidden;
+ border-radius: inherit;
+ }
+`;
diff --git a/packages/webawesome/src/components/avatar/avatar.ts b/packages/webawesome/src/components/avatar/avatar.ts
index 7dc7a1530..4b2c983c9 100644
--- a/packages/webawesome/src/components/avatar/avatar.ts
+++ b/packages/webawesome/src/components/avatar/avatar.ts
@@ -4,7 +4,7 @@ import { WaErrorEvent } from '../../events/error.js';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import '../icon/icon.js';
-import styles from './avatar.css';
+import styles from './avatar.styles.js';
/**
* @summary Avatars are used to represent a person or object.
diff --git a/packages/webawesome/src/components/badge/badge.css b/packages/webawesome/src/components/badge/badge.css
deleted file mode 100644
index 6fceaac75..000000000
--- a/packages/webawesome/src/components/badge/badge.css
+++ /dev/null
@@ -1,104 +0,0 @@
-:host {
- --pulse-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
-
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 0.375em 0.625em;
- color: var(--wa-color-on-loud, var(--wa-color-brand-on-loud));
- font-size: max(var(--wa-font-size-2xs), 0.75em);
- font-weight: var(--wa-font-weight-semibold);
- line-height: 1;
- white-space: nowrap;
- background-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
- border-color: transparent;
- border-radius: var(--wa-border-radius-s);
- border-style: var(--wa-border-style);
- border-width: var(--wa-border-width-s);
- user-select: none;
- -webkit-user-select: none;
- cursor: inherit;
-}
-
-/* Appearance modifiers */
-:host([appearance='outlined']) {
- --pulse-color: var(--wa-color-border-loud, var(--wa-color-brand-border-loud));
-
- color: var(--wa-color-on-quiet, var(--wa-color-brand-on-quiet));
- background-color: transparent;
- border-color: var(--wa-color-border-loud, var(--wa-color-brand-border-loud));
-}
-
-:host([appearance='filled']) {
- --pulse-color: var(--wa-color-fill-normal, var(--wa-color-brand-fill-normal));
-
- color: var(--wa-color-on-normal, var(--wa-color-brand-on-normal));
- background-color: var(--wa-color-fill-normal, var(--wa-color-brand-fill-normal));
- border-color: transparent;
-}
-
-:host([appearance='filled-outlined']) {
- --pulse-color: var(--wa-color-border-normal, var(--wa-color-brand-border-normal));
-
- color: var(--wa-color-on-normal, var(--wa-color-brand-on-normal));
- background-color: var(--wa-color-fill-normal, var(--wa-color-brand-fill-normal));
- border-color: var(--wa-color-border-normal, var(--wa-color-brand-border-normal));
-}
-
-:host([appearance='accent']) {
- --pulse-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
-
- color: var(--wa-color-on-loud, var(--wa-color-brand-on-loud));
- background-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
- border-color: transparent;
-}
-
-/* Pill modifier */
-:host([pill]) {
- border-radius: var(--wa-border-radius-pill);
-}
-
-/* Pulse attention */
-:host([attention='pulse']) {
- animation: pulse 1.5s infinite;
-}
-
-@keyframes pulse {
- 0% {
- box-shadow: 0 0 0 0 var(--pulse-color);
- }
- 70% {
- box-shadow: 0 0 0 0.5rem transparent;
- }
- 100% {
- box-shadow: 0 0 0 0 transparent;
- }
-}
-
-/* Bounce attention */
-:host([attention='bounce']) {
- animation: bounce 1s cubic-bezier(0.28, 0.84, 0.42, 1) infinite;
-}
-
-@keyframes bounce {
- 0%,
- 20%,
- 50%,
- 80%,
- 100% {
- transform: translateY(0);
- }
- 40% {
- transform: translateY(-5px);
- }
- 60% {
- transform: translateY(-2px);
- }
-}
-
-::slotted(wa-icon) {
- margin-inline-end: var(--wa-space-2xs, 0.25em);
- opacity: 90%;
- line-height: 1;
- height: 0.85em;
-}
diff --git a/packages/webawesome/src/components/badge/badge.styles.ts b/packages/webawesome/src/components/badge/badge.styles.ts
new file mode 100644
index 000000000..14147861e
--- /dev/null
+++ b/packages/webawesome/src/components/badge/badge.styles.ts
@@ -0,0 +1,108 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --pulse-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
+
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.375em 0.625em;
+ color: var(--wa-color-on-loud, var(--wa-color-brand-on-loud));
+ font-size: max(var(--wa-font-size-2xs), 0.75em);
+ font-weight: var(--wa-font-weight-semibold);
+ line-height: 1;
+ white-space: nowrap;
+ background-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
+ border-color: transparent;
+ border-radius: var(--wa-border-radius-s);
+ border-style: var(--wa-border-style);
+ border-width: var(--wa-border-width-s);
+ user-select: none;
+ -webkit-user-select: none;
+ cursor: inherit;
+ }
+
+ /* Appearance modifiers */
+ :host([appearance='outlined']) {
+ --pulse-color: var(--wa-color-border-loud, var(--wa-color-brand-border-loud));
+
+ color: var(--wa-color-on-quiet, var(--wa-color-brand-on-quiet));
+ background-color: transparent;
+ border-color: var(--wa-color-border-loud, var(--wa-color-brand-border-loud));
+ }
+
+ :host([appearance='filled']) {
+ --pulse-color: var(--wa-color-fill-normal, var(--wa-color-brand-fill-normal));
+
+ color: var(--wa-color-on-normal, var(--wa-color-brand-on-normal));
+ background-color: var(--wa-color-fill-normal, var(--wa-color-brand-fill-normal));
+ border-color: transparent;
+ }
+
+ :host([appearance='filled-outlined']) {
+ --pulse-color: var(--wa-color-border-normal, var(--wa-color-brand-border-normal));
+
+ color: var(--wa-color-on-normal, var(--wa-color-brand-on-normal));
+ background-color: var(--wa-color-fill-normal, var(--wa-color-brand-fill-normal));
+ border-color: var(--wa-color-border-normal, var(--wa-color-brand-border-normal));
+ }
+
+ :host([appearance='accent']) {
+ --pulse-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
+
+ color: var(--wa-color-on-loud, var(--wa-color-brand-on-loud));
+ background-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
+ border-color: transparent;
+ }
+
+ /* Pill modifier */
+ :host([pill]) {
+ border-radius: var(--wa-border-radius-pill);
+ }
+
+ /* Pulse attention */
+ :host([attention='pulse']) {
+ animation: pulse 1.5s infinite;
+ }
+
+ @keyframes pulse {
+ 0% {
+ box-shadow: 0 0 0 0 var(--pulse-color);
+ }
+ 70% {
+ box-shadow: 0 0 0 0.5rem transparent;
+ }
+ 100% {
+ box-shadow: 0 0 0 0 transparent;
+ }
+ }
+
+ /* Bounce attention */
+ :host([attention='bounce']) {
+ animation: bounce 1s cubic-bezier(0.28, 0.84, 0.42, 1) infinite;
+ }
+
+ @keyframes bounce {
+ 0%,
+ 20%,
+ 50%,
+ 80%,
+ 100% {
+ transform: translateY(0);
+ }
+ 40% {
+ transform: translateY(-5px);
+ }
+ 60% {
+ transform: translateY(-2px);
+ }
+ }
+
+ ::slotted(wa-icon) {
+ margin-inline-end: var(--wa-space-2xs, 0.25em);
+ opacity: 90%;
+ line-height: 1;
+ height: 0.85em;
+ }
+`;
diff --git a/packages/webawesome/src/components/badge/badge.ts b/packages/webawesome/src/components/badge/badge.ts
index 13c3fa375..892eadb27 100644
--- a/packages/webawesome/src/components/badge/badge.ts
+++ b/packages/webawesome/src/components/badge/badge.ts
@@ -1,8 +1,8 @@
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import variantStyles from '../../styles/utilities/variants.css';
-import styles from './badge.css';
+import variantStyles from '../../styles/component/variants.styles.js';
+import styles from './badge.styles.js';
/**
* @summary Badges are used to draw attention and display statuses or counts.
diff --git a/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.css b/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.css
deleted file mode 100644
index 4784fa017..000000000
--- a/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.css
+++ /dev/null
@@ -1,81 +0,0 @@
-:host {
- color: var(--wa-color-text-link);
- display: inline-flex;
- align-items: center;
- font: inherit;
- font-weight: var(--wa-font-weight-action);
- line-height: var(--wa-line-height-normal);
- white-space: nowrap;
-}
-
-:host(:last-of-type) {
- color: var(--wa-color-text-quiet);
-}
-
-.label {
- display: inline-block;
- font: inherit;
- text-decoration: none;
- color: currentColor;
- background: none;
- border: none;
- border-radius: var(--wa-border-radius-m);
- padding: 0;
- margin: 0;
- cursor: pointer;
- transition: color var(--wa-transition-normal) var(--wa-transition-easing);
-}
-
-@media (hover: hover) {
- :host(:not(:last-of-type)) .label:hover {
- color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
- }
-}
-
-:host(:not(:last-of-type)) .label:active {
- color: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
-}
-
-.label:focus {
- outline: none;
-}
-
-.label:focus-visible {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
-}
-
-.start,
-.end {
- display: none;
- flex: 0 0 auto;
- display: flex;
- align-items: center;
-}
-
-.start,
-.end {
- display: inline-flex;
- color: var(--wa-color-text-quiet);
-}
-
-::slotted([slot='start']) {
- margin-inline-end: var(--wa-space-s);
-}
-
-::slotted([slot='end']) {
- margin-inline-start: var(--wa-space-s);
-}
-
-:host(:last-of-type) .separator {
- display: none;
-}
-
-.separator {
- color: var(--wa-color-text-quiet);
- display: inline-flex;
- align-items: center;
- margin: 0 var(--wa-space-s);
- user-select: none;
- -webkit-user-select: none;
-}
diff --git a/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.styles.ts b/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.styles.ts
new file mode 100644
index 000000000..b7370f626
--- /dev/null
+++ b/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.styles.ts
@@ -0,0 +1,85 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ color: var(--wa-color-text-link);
+ display: inline-flex;
+ align-items: center;
+ font: inherit;
+ font-weight: var(--wa-font-weight-action);
+ line-height: var(--wa-line-height-normal);
+ white-space: nowrap;
+ }
+
+ :host(:last-of-type) {
+ color: var(--wa-color-text-quiet);
+ }
+
+ .label {
+ display: inline-block;
+ font: inherit;
+ text-decoration: none;
+ color: currentColor;
+ background: none;
+ border: none;
+ border-radius: var(--wa-border-radius-m);
+ padding: 0;
+ margin: 0;
+ cursor: pointer;
+ transition: color var(--wa-transition-normal) var(--wa-transition-easing);
+ }
+
+ @media (hover: hover) {
+ :host(:not(:last-of-type)) .label:hover {
+ color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
+ }
+ }
+
+ :host(:not(:last-of-type)) .label:active {
+ color: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
+ }
+
+ .label:focus {
+ outline: none;
+ }
+
+ .label:focus-visible {
+ outline: var(--wa-focus-ring);
+ outline-offset: var(--wa-focus-ring-offset);
+ }
+
+ .start,
+ .end {
+ display: none;
+ flex: 0 0 auto;
+ display: flex;
+ align-items: center;
+ }
+
+ .start,
+ .end {
+ display: inline-flex;
+ color: var(--wa-color-text-quiet);
+ }
+
+ ::slotted([slot='start']) {
+ margin-inline-end: var(--wa-space-s);
+ }
+
+ ::slotted([slot='end']) {
+ margin-inline-start: var(--wa-space-s);
+ }
+
+ :host(:last-of-type) .separator {
+ display: none;
+ }
+
+ .separator {
+ color: var(--wa-color-text-quiet);
+ display: inline-flex;
+ align-items: center;
+ margin: 0 var(--wa-space-s);
+ user-select: none;
+ -webkit-user-select: none;
+ }
+`;
diff --git a/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.ts b/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.ts
index ff9c8b29d..83ec368c5 100644
--- a/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.ts
+++ b/packages/webawesome/src/components/breadcrumb-item/breadcrumb-item.ts
@@ -3,7 +3,7 @@ import { customElement, property, query, state } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import styles from './breadcrumb-item.css';
+import styles from './breadcrumb-item.styles.js';
/**
* @summary Breadcrumb Items are used inside breadcrumbs to represent different links.
diff --git a/packages/webawesome/src/components/breadcrumb/breadcrumb.css b/packages/webawesome/src/components/breadcrumb/breadcrumb.css
deleted file mode 100644
index a45d4f776..000000000
--- a/packages/webawesome/src/components/breadcrumb/breadcrumb.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.breadcrumb {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
-}
diff --git a/packages/webawesome/src/components/breadcrumb/breadcrumb.styles.ts b/packages/webawesome/src/components/breadcrumb/breadcrumb.styles.ts
new file mode 100644
index 000000000..86c0e16e0
--- /dev/null
+++ b/packages/webawesome/src/components/breadcrumb/breadcrumb.styles.ts
@@ -0,0 +1,9 @@
+import { css } from 'lit';
+
+export default css`
+ .breadcrumb {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ }
+`;
diff --git a/packages/webawesome/src/components/breadcrumb/breadcrumb.ts b/packages/webawesome/src/components/breadcrumb/breadcrumb.ts
index be3e24a24..0f2f77093 100644
--- a/packages/webawesome/src/components/breadcrumb/breadcrumb.ts
+++ b/packages/webawesome/src/components/breadcrumb/breadcrumb.ts
@@ -4,7 +4,7 @@ import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
import type WaBreadcrumbItem from '../breadcrumb-item/breadcrumb-item.js';
import '../icon/icon.js';
-import styles from './breadcrumb.css';
+import styles from './breadcrumb.styles.js';
/**
* @summary Breadcrumbs provide a group of links so users can easily navigate a website's hierarchy.
diff --git a/packages/webawesome/src/components/button-group/button-group.css b/packages/webawesome/src/components/button-group/button-group.css
deleted file mode 100644
index ee7e152c0..000000000
--- a/packages/webawesome/src/components/button-group/button-group.css
+++ /dev/null
@@ -1,44 +0,0 @@
-:host {
- display: inline-flex;
-}
-
-.button-group {
- display: flex;
- position: relative;
- isolation: isolate;
- flex-wrap: wrap;
- gap: 1px;
-
- @media (hover: hover) {
- > :hover,
- &::slotted(:hover) {
- z-index: 1;
- }
- }
-
- /* Focus and checked are always on top */
- > :focus,
- &::slotted(:focus),
- > [aria-checked='true'],
- &::slotted([aria-checked='true']),
- > [checked],
- &::slotted([checked]) {
- z-index: 2 !important;
- }
-}
-:host([orientation='vertical']) .button-group {
- flex-direction: column;
-}
-
-/* Button groups with at least one outlined button will not have a gap and instead have borders overlap */
-.button-group.has-outlined {
- gap: 0;
-
- &:not([aria-orientation='vertical']):not(.button-group-vertical)::slotted(:not(:first-child)) {
- margin-inline-start: calc(-1 * var(--border-width));
- }
-
- &:is([aria-orientation='vertical'], .button-group-vertical)::slotted(:not(:first-child)) {
- margin-block-start: calc(-1 * var(--border-width));
- }
-}
diff --git a/packages/webawesome/src/components/button-group/button-group.styles.ts b/packages/webawesome/src/components/button-group/button-group.styles.ts
new file mode 100644
index 000000000..dfa4d85c9
--- /dev/null
+++ b/packages/webawesome/src/components/button-group/button-group.styles.ts
@@ -0,0 +1,48 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ display: inline-flex;
+ }
+
+ .button-group {
+ display: flex;
+ position: relative;
+ isolation: isolate;
+ flex-wrap: wrap;
+ gap: 1px;
+
+ @media (hover: hover) {
+ > :hover,
+ &::slotted(:hover) {
+ z-index: 1;
+ }
+ }
+
+ /* Focus and checked are always on top */
+ > :focus,
+ &::slotted(:focus),
+ > [aria-checked='true'],
+ &::slotted([aria-checked='true']),
+ > [checked],
+ &::slotted([checked]) {
+ z-index: 2 !important;
+ }
+ }
+ :host([orientation='vertical']) .button-group {
+ flex-direction: column;
+ }
+
+ /* Button groups with at least one outlined button will not have a gap and instead have borders overlap */
+ .button-group.has-outlined {
+ gap: 0;
+
+ &:not([aria-orientation='vertical']):not(.button-group-vertical)::slotted(:not(:first-child)) {
+ margin-inline-start: calc(-1 * var(--border-width));
+ }
+
+ &:is([aria-orientation='vertical'], .button-group-vertical)::slotted(:not(:first-child)) {
+ margin-block-start: calc(-1 * var(--border-width));
+ }
+ }
+`;
diff --git a/packages/webawesome/src/components/button-group/button-group.ts b/packages/webawesome/src/components/button-group/button-group.ts
index 0a491c3ad..cda7123c5 100644
--- a/packages/webawesome/src/components/button-group/button-group.ts
+++ b/packages/webawesome/src/components/button-group/button-group.ts
@@ -4,7 +4,7 @@ import { customElement, property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import type WaButton from '../button/button.js';
-import styles from './button-group.css';
+import styles from './button-group.styles.js';
/**
* @summary Button groups can be used to group related buttons into sections.
diff --git a/packages/webawesome/src/components/button/button.css b/packages/webawesome/src/components/button/button.css
deleted file mode 100644
index 7660f5c0e..000000000
--- a/packages/webawesome/src/components/button/button.css
+++ /dev/null
@@ -1,372 +0,0 @@
-@layer wa-component {
- :host {
- display: inline-block;
-
- /* Workaround because Chrome doesn't like :host(:has()) below
- * https://issues.chromium.org/issues/40062355
- * Firefox doesn't like this nested rule, so both are needed */
- &:has(wa-badge) {
- position: relative;
- }
- }
-
- /* Apply relative positioning only when needed to position wa-badge
- * This avoids creating a new stacking context for every button */
- :host(:has(wa-badge)) {
- position: relative;
- }
-}
-
-.button {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- text-decoration: none;
- user-select: none;
- -webkit-user-select: none;
- white-space: nowrap;
- vertical-align: middle;
- transition-property: background, border, box-shadow, color;
- transition-duration: var(--wa-transition-fast);
- transition-timing-function: var(--wa-transition-easing);
- cursor: pointer;
- padding: 0 var(--wa-form-control-padding-inline);
- font-family: inherit;
- font-size: inherit;
- font-weight: var(--wa-font-weight-action);
- line-height: calc(var(--wa-form-control-height) - var(--border-width) * 2);
- height: var(--wa-form-control-height);
- width: 100%;
-
- background-color: var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud));
- border-color: transparent;
- color: var(--wa-color-on-loud, var(--wa-color-neutral-on-loud));
- border-radius: var(--wa-form-control-border-radius);
- border-style: var(--wa-border-style);
- border-width: var(--wa-border-width-s);
-}
-
-/* Appearance modifiers */
-:host([appearance='plain']) {
- .button {
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- background-color: transparent;
- border-color: transparent;
- }
- @media (hover: hover) {
- .button:not(.disabled):not(.loading):hover {
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
- }
- }
- .button:not(.disabled):not(.loading):active {
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- background-color: color-mix(
- in oklab,
- var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet)),
- var(--wa-color-mix-active)
- );
- }
-}
-
-:host([appearance='outlined']) {
- .button {
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- background-color: transparent;
- border-color: var(--wa-color-border-loud, var(--wa-color-neutral-border-loud));
- }
- @media (hover: hover) {
- .button:not(.disabled):not(.loading):hover {
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
- }
- }
- .button:not(.disabled):not(.loading):active {
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- background-color: color-mix(
- in oklab,
- var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet)),
- var(--wa-color-mix-active)
- );
- }
-}
-
-:host([appearance='filled']) {
- .button {
- color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
- background-color: var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal));
- border-color: transparent;
- }
- @media (hover: hover) {
- .button:not(.disabled):not(.loading):hover {
- color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
- background-color: color-mix(
- in oklab,
- var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
- var(--wa-color-mix-hover)
- );
- }
- }
- .button:not(.disabled):not(.loading):active {
- color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
- background-color: color-mix(
- in oklab,
- var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
- var(--wa-color-mix-active)
- );
- }
-}
-
-:host([appearance='filled-outlined']) {
- .button {
- color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
- background-color: var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal));
- border-color: var(--wa-color-border-normal, var(--wa-color-neutral-border-normal));
- }
- @media (hover: hover) {
- .button:not(.disabled):not(.loading):hover {
- color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
- background-color: color-mix(
- in oklab,
- var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
- var(--wa-color-mix-hover)
- );
- }
- }
- .button:not(.disabled):not(.loading):active {
- color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
- background-color: color-mix(
- in oklab,
- var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
- var(--wa-color-mix-active)
- );
- }
-}
-
-:host([appearance='accent']) {
- .button {
- color: var(--wa-color-on-loud, var(--wa-color-neutral-on-loud));
- background-color: var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud));
- border-color: transparent;
- }
- @media (hover: hover) {
- .button:not(.disabled):not(.loading):hover {
- background-color: color-mix(
- in oklab,
- var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud)),
- var(--wa-color-mix-hover)
- );
- }
- }
- .button:not(.disabled):not(.loading):active {
- background-color: color-mix(
- in oklab,
- var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud)),
- var(--wa-color-mix-active)
- );
- }
-}
-
-/* Focus states */
-.button:focus {
- outline: none;
-}
-
-.button:focus-visible {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
-}
-
-/* Disabled state */
-.button.disabled {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-/* When disabled, prevent mouse events from bubbling up from children */
-.button.disabled * {
- pointer-events: none;
-}
-
-/* Keep it last so Safari doesn't stop parsing this block */
-.button::-moz-focus-inner {
- border: 0;
-}
-
-/* Icon buttons */
-.button.is-icon-button {
- outline-offset: 2px;
- width: var(--wa-form-control-height);
- aspect-ratio: 1;
-}
-
-.button.is-icon-button:has(wa-icon) {
- width: auto;
-}
-
-/* Pill modifier */
-:host([pill]) .button {
- border-radius: var(--wa-border-radius-pill);
-}
-
-/*
- * Label
- */
-
-.start,
-.end {
- flex: 0 0 auto;
- display: flex;
- align-items: center;
- pointer-events: none;
-}
-
-.label {
- display: inline-block;
-}
-
-.is-icon-button .label {
- display: flex;
-}
-
-.label::slotted(wa-icon) {
- align-self: center;
-}
-
-/*
- * Caret modifier
- */
-
-wa-icon[part='caret'] {
- display: flex;
- align-self: center;
- align-items: center;
-
- &::part(svg) {
- width: 0.875em;
- height: 0.875em;
- }
-
- .button:has(&) .end {
- display: none;
- }
-}
-
-/*
- * Loading modifier
- */
-
-.loading {
- position: relative;
- cursor: wait;
-
- .start,
- .label,
- .end,
- .caret {
- visibility: hidden;
- }
-
- wa-spinner {
- --indicator-color: currentColor;
- --track-color: color-mix(in oklab, currentColor, transparent 90%);
-
- position: absolute;
- font-size: 1em;
- height: 1em;
- width: 1em;
- top: calc(50% - 0.5em);
- left: calc(50% - 0.5em);
- }
-}
-
-/*
- * Badges
- */
-
-.button ::slotted(wa-badge) {
- border-color: var(--wa-color-surface-default);
- position: absolute;
- inset-block-start: 0;
- inset-inline-end: 0;
- translate: 50% -50%;
- pointer-events: none;
-}
-
-:host(:dir(rtl)) ::slotted(wa-badge) {
- translate: -50% -50%;
-}
-
-/*
-* Button spacing
-*/
-
-slot[name='start']::slotted(*) {
- margin-inline-end: 0.75em;
-}
-
-slot[name='end']::slotted(*),
-.button:not(.visually-hidden-label) [part='caret'] {
- margin-inline-start: 0.75em;
-}
-
-/*
- * Button group border radius modifications
- */
-
-/* Remove border radius from all grouped buttons by default */
-:host(.wa-button-group__button) .button {
- border-radius: 0;
-}
-
-/* Horizontal orientation */
-:host(.wa-button-group__horizontal.wa-button-group__button-first) .button {
- border-start-start-radius: var(--wa-form-control-border-radius);
- border-end-start-radius: var(--wa-form-control-border-radius);
-}
-
-:host(.wa-button-group__horizontal.wa-button-group__button-last) .button {
- border-start-end-radius: var(--wa-form-control-border-radius);
- border-end-end-radius: var(--wa-form-control-border-radius);
-}
-
-/* Vertical orientation */
-:host(.wa-button-group__vertical) {
- flex: 1 1 auto;
-}
-
-:host(.wa-button-group__vertical) .button {
- width: 100%;
- justify-content: start;
-}
-
-:host(.wa-button-group__vertical.wa-button-group__button-first) .button {
- border-start-start-radius: var(--wa-form-control-border-radius);
- border-start-end-radius: var(--wa-form-control-border-radius);
-}
-
-:host(.wa-button-group__vertical.wa-button-group__button-last) .button {
- border-end-start-radius: var(--wa-form-control-border-radius);
- border-end-end-radius: var(--wa-form-control-border-radius);
-}
-
-/* Handle pill modifier for button groups */
-:host([pill].wa-button-group__horizontal.wa-button-group__button-first) .button {
- border-start-start-radius: var(--wa-border-radius-pill);
- border-end-start-radius: var(--wa-border-radius-pill);
-}
-
-:host([pill].wa-button-group__horizontal.wa-button-group__button-last) .button {
- border-start-end-radius: var(--wa-border-radius-pill);
- border-end-end-radius: var(--wa-border-radius-pill);
-}
-
-:host([pill].wa-button-group__vertical.wa-button-group__button-first) .button {
- border-start-start-radius: var(--wa-border-radius-pill);
- border-start-end-radius: var(--wa-border-radius-pill);
-}
-
-:host([pill].wa-button-group__vertical.wa-button-group__button-last) .button {
- border-end-start-radius: var(--wa-border-radius-pill);
- border-end-end-radius: var(--wa-border-radius-pill);
-}
diff --git a/packages/webawesome/src/components/button/button.styles.ts b/packages/webawesome/src/components/button/button.styles.ts
new file mode 100644
index 000000000..2cd955897
--- /dev/null
+++ b/packages/webawesome/src/components/button/button.styles.ts
@@ -0,0 +1,376 @@
+import { css } from 'lit';
+
+export default css`
+ @layer wa-component {
+ :host {
+ display: inline-block;
+
+ /* Workaround because Chrome doesn't like :host(:has()) below
+ * https://issues.chromium.org/issues/40062355
+ * Firefox doesn't like this nested rule, so both are needed */
+ &:has(wa-badge) {
+ position: relative;
+ }
+ }
+
+ /* Apply relative positioning only when needed to position wa-badge
+ * This avoids creating a new stacking context for every button */
+ :host(:has(wa-badge)) {
+ position: relative;
+ }
+ }
+
+ .button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ text-decoration: none;
+ user-select: none;
+ -webkit-user-select: none;
+ white-space: nowrap;
+ vertical-align: middle;
+ transition-property: background, border, box-shadow, color, opacity;
+ transition-duration: var(--wa-transition-fast);
+ transition-timing-function: var(--wa-transition-easing);
+ cursor: pointer;
+ padding: 0 var(--wa-form-control-padding-inline);
+ font-family: inherit;
+ font-size: inherit;
+ font-weight: var(--wa-font-weight-action);
+ line-height: calc(var(--wa-form-control-height) - var(--border-width) * 2);
+ height: var(--wa-form-control-height);
+ width: 100%;
+
+ background-color: var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud));
+ border-color: transparent;
+ color: var(--wa-color-on-loud, var(--wa-color-neutral-on-loud));
+ border-radius: var(--wa-form-control-border-radius);
+ border-style: var(--wa-border-style);
+ border-width: var(--wa-border-width-s);
+ }
+
+ /* Appearance modifiers */
+ :host([appearance='plain']) {
+ .button {
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ background-color: transparent;
+ border-color: transparent;
+ }
+ @media (hover: hover) {
+ .button:not(.disabled):not(.loading):hover {
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+ }
+ }
+ .button:not(.disabled):not(.loading):active {
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ background-color: color-mix(
+ in oklab,
+ var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet)),
+ var(--wa-color-mix-active)
+ );
+ }
+ }
+
+ :host([appearance='outlined']) {
+ .button {
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ background-color: transparent;
+ border-color: var(--wa-color-border-loud, var(--wa-color-neutral-border-loud));
+ }
+ @media (hover: hover) {
+ .button:not(.disabled):not(.loading):hover {
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+ }
+ }
+ .button:not(.disabled):not(.loading):active {
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ background-color: color-mix(
+ in oklab,
+ var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet)),
+ var(--wa-color-mix-active)
+ );
+ }
+ }
+
+ :host([appearance='filled']) {
+ .button {
+ color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+ background-color: var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal));
+ border-color: transparent;
+ }
+ @media (hover: hover) {
+ .button:not(.disabled):not(.loading):hover {
+ color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+ background-color: color-mix(
+ in oklab,
+ var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
+ var(--wa-color-mix-hover)
+ );
+ }
+ }
+ .button:not(.disabled):not(.loading):active {
+ color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+ background-color: color-mix(
+ in oklab,
+ var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
+ var(--wa-color-mix-active)
+ );
+ }
+ }
+
+ :host([appearance='filled-outlined']) {
+ .button {
+ color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+ background-color: var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal));
+ border-color: var(--wa-color-border-normal, var(--wa-color-neutral-border-normal));
+ }
+ @media (hover: hover) {
+ .button:not(.disabled):not(.loading):hover {
+ color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+ background-color: color-mix(
+ in oklab,
+ var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
+ var(--wa-color-mix-hover)
+ );
+ }
+ }
+ .button:not(.disabled):not(.loading):active {
+ color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+ background-color: color-mix(
+ in oklab,
+ var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
+ var(--wa-color-mix-active)
+ );
+ }
+ }
+
+ :host([appearance='accent']) {
+ .button {
+ color: var(--wa-color-on-loud, var(--wa-color-neutral-on-loud));
+ background-color: var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud));
+ border-color: transparent;
+ }
+ @media (hover: hover) {
+ .button:not(.disabled):not(.loading):hover {
+ background-color: color-mix(
+ in oklab,
+ var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud)),
+ var(--wa-color-mix-hover)
+ );
+ }
+ }
+ .button:not(.disabled):not(.loading):active {
+ background-color: color-mix(
+ in oklab,
+ var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud)),
+ var(--wa-color-mix-active)
+ );
+ }
+ }
+
+ /* Focus states */
+ .button:focus {
+ outline: none;
+ }
+
+ .button:focus-visible {
+ outline: var(--wa-focus-ring);
+ outline-offset: var(--wa-focus-ring-offset);
+ }
+
+ /* Disabled state */
+ .button.disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ /* When disabled, prevent mouse events from bubbling up from children */
+ .button.disabled * {
+ pointer-events: none;
+ }
+
+ /* Keep it last so Safari doesn't stop parsing this block */
+ .button::-moz-focus-inner {
+ border: 0;
+ }
+
+ /* Icon buttons */
+ .button.is-icon-button {
+ outline-offset: 2px;
+ width: var(--wa-form-control-height);
+ aspect-ratio: 1;
+ }
+
+ .button.is-icon-button:has(wa-icon) {
+ width: auto;
+ }
+
+ /* Pill modifier */
+ :host([pill]) .button {
+ border-radius: var(--wa-border-radius-pill);
+ }
+
+ /*
+ * Label
+ */
+
+ .start,
+ .end {
+ flex: 0 0 auto;
+ display: flex;
+ align-items: center;
+ pointer-events: none;
+ }
+
+ .label {
+ display: inline-block;
+ }
+
+ .is-icon-button .label {
+ display: flex;
+ }
+
+ .label::slotted(wa-icon) {
+ align-self: center;
+ }
+
+ /*
+ * Caret modifier
+ */
+
+ wa-icon[part='caret'] {
+ display: flex;
+ align-self: center;
+ align-items: center;
+
+ &::part(svg) {
+ width: 0.875em;
+ height: 0.875em;
+ }
+
+ .button:has(&) .end {
+ display: none;
+ }
+ }
+
+ /*
+ * Loading modifier
+ */
+
+ .loading {
+ position: relative;
+ cursor: wait;
+
+ .start,
+ .label,
+ .end,
+ .caret {
+ visibility: hidden;
+ }
+
+ wa-spinner {
+ --indicator-color: currentColor;
+ --track-color: color-mix(in oklab, currentColor, transparent 90%);
+
+ position: absolute;
+ font-size: 1em;
+ height: 1em;
+ width: 1em;
+ top: calc(50% - 0.5em);
+ left: calc(50% - 0.5em);
+ }
+ }
+
+ /*
+ * Badges
+ */
+
+ .button ::slotted(wa-badge) {
+ border-color: var(--wa-color-surface-default);
+ position: absolute;
+ inset-block-start: 0;
+ inset-inline-end: 0;
+ translate: 50% -50%;
+ pointer-events: none;
+ }
+
+ :host(:dir(rtl)) ::slotted(wa-badge) {
+ translate: -50% -50%;
+ }
+
+ /*
+ * Button spacing
+ */
+
+ slot[name='start']::slotted(*) {
+ margin-inline-end: 0.75em;
+ }
+
+ slot[name='end']::slotted(*),
+ .button:not(.visually-hidden-label) [part='caret'] {
+ margin-inline-start: 0.75em;
+ }
+
+ /*
+ * Button group border radius modifications
+ */
+
+ /* Remove border radius from all grouped buttons by default */
+ :host(.wa-button-group__button) .button {
+ border-radius: 0;
+ }
+
+ /* Horizontal orientation */
+ :host(.wa-button-group__horizontal.wa-button-group__button-first) .button {
+ border-start-start-radius: var(--wa-form-control-border-radius);
+ border-end-start-radius: var(--wa-form-control-border-radius);
+ }
+
+ :host(.wa-button-group__horizontal.wa-button-group__button-last) .button {
+ border-start-end-radius: var(--wa-form-control-border-radius);
+ border-end-end-radius: var(--wa-form-control-border-radius);
+ }
+
+ /* Vertical orientation */
+ :host(.wa-button-group__vertical) {
+ flex: 1 1 auto;
+ }
+
+ :host(.wa-button-group__vertical) .button {
+ width: 100%;
+ justify-content: start;
+ }
+
+ :host(.wa-button-group__vertical.wa-button-group__button-first) .button {
+ border-start-start-radius: var(--wa-form-control-border-radius);
+ border-start-end-radius: var(--wa-form-control-border-radius);
+ }
+
+ :host(.wa-button-group__vertical.wa-button-group__button-last) .button {
+ border-end-start-radius: var(--wa-form-control-border-radius);
+ border-end-end-radius: var(--wa-form-control-border-radius);
+ }
+
+ /* Handle pill modifier for button groups */
+ :host([pill].wa-button-group__horizontal.wa-button-group__button-first) .button {
+ border-start-start-radius: var(--wa-border-radius-pill);
+ border-end-start-radius: var(--wa-border-radius-pill);
+ }
+
+ :host([pill].wa-button-group__horizontal.wa-button-group__button-last) .button {
+ border-start-end-radius: var(--wa-border-radius-pill);
+ border-end-end-radius: var(--wa-border-radius-pill);
+ }
+
+ :host([pill].wa-button-group__vertical.wa-button-group__button-first) .button {
+ border-start-start-radius: var(--wa-border-radius-pill);
+ border-start-end-radius: var(--wa-border-radius-pill);
+ }
+
+ :host([pill].wa-button-group__vertical.wa-button-group__button-last) .button {
+ border-end-start-radius: var(--wa-border-radius-pill);
+ border-end-end-radius: var(--wa-border-radius-pill);
+ }
+`;
diff --git a/packages/webawesome/src/components/button/button.ts b/packages/webawesome/src/components/button/button.ts
index 6c1225559..146fdb83d 100644
--- a/packages/webawesome/src/components/button/button.ts
+++ b/packages/webawesome/src/components/button/button.ts
@@ -7,13 +7,13 @@ import { HasSlotController } from '../../internal/slot.js';
import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
-import sizeStyles from '../../styles/utilities/size.css';
-import variantStyles from '../../styles/utilities/variants.css';
+import sizeStyles from '../../styles/component/size.styles.js';
+import variantStyles from '../../styles/component/variants.styles.js';
import { LocalizeController } from '../../utilities/localize.js';
import '../icon/icon.js';
import type WaIcon from '../icon/icon.js';
import '../spinner/spinner.js';
-import styles from './button.css';
+import styles from './button.styles.js';
/**
* @summary Buttons represent actions that are available to the user.
@@ -115,7 +115,6 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
* The "form owner" to associate the button with. If omitted, the closest containing form will be used instead. The
* value of this attribute must be an id of a form in the same document or shadow root as the button.
*/
- @property({ reflect: true }) form: string | null = null;
/** Used to override the form owner's `action` attribute. */
@property({ attribute: 'formaction' }) formAction: string;
@@ -135,24 +134,27 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
private constructLightDOMButton() {
const button = document.createElement('button');
+
+ for (const attribute of this.attributes) {
+ if (attribute.name === 'style') {
+ // Skip style attributes as they *shouldn't* be necessary
+ continue;
+ }
+ button.setAttribute(attribute.name, attribute.value);
+ }
+
button.type = this.type;
- button.style.position = 'absolute';
- button.style.width = '0';
- button.style.height = '0';
- button.style.clipPath = 'inset(50%)';
- button.style.overflow = 'hidden';
- button.style.whiteSpace = 'nowrap';
+ button.style.position = 'absolute !important';
+ button.style.width = '0 !important';
+ button.style.height = '0 !important';
+ button.style.clipPath = 'inset(50%) !important';
+ button.style.overflow = 'hidden !important';
+ button.style.whiteSpace = 'nowrap !important';
if (this.name) {
button.name = this.name;
}
button.value = this.value || '';
- ['form', 'formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget'].forEach(attr => {
- if (this.hasAttribute(attr)) {
- button.setAttribute(attr, this.getAttribute(attr)!);
- }
- });
-
return button;
}
diff --git a/packages/webawesome/src/components/callout/callout.css b/packages/webawesome/src/components/callout/callout.css
deleted file mode 100644
index b7118c2e4..000000000
--- a/packages/webawesome/src/components/callout/callout.css
+++ /dev/null
@@ -1,60 +0,0 @@
-:host {
- display: flex;
- position: relative;
- align-items: stretch;
- border-radius: var(--wa-panel-border-radius);
- background-color: var(--wa-color-fill-quiet, var(--wa-color-brand-fill-quiet));
- border-color: var(--wa-color-border-quiet, var(--wa-color-brand-border-quiet));
- border-style: var(--wa-panel-border-style);
- border-width: var(--wa-panel-border-width);
- color: var(--wa-color-text-normal);
- padding: 1em;
-}
-
-/* Appearance modifiers */
-:host([appearance~='plain']) {
- background-color: transparent;
- border-color: transparent;
-}
-
-:host([appearance~='outlined']) {
- background-color: transparent;
- border-color: var(--wa-color-border-loud, var(--wa-color-brand-border-loud));
-}
-
-:host([appearance~='filled']) {
- background-color: var(--wa-color-fill-quiet, var(--wa-color-brand-fill-quiet));
- border-color: transparent;
-}
-
-:host([appearance~='filled-outlined']) {
- border-color: var(--wa-color-border-quiet, var(--wa-color-brand-border-quiet));
-}
-
-:host([appearance~='accent']) {
- color: var(--wa-color-on-loud, var(--wa-color-brand-on-loud));
- background-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
- border-color: transparent;
-
- [part~='icon'] {
- color: currentColor;
- }
-}
-
-[part~='icon'] {
- flex: 0 0 auto;
- display: flex;
- align-items: center;
- color: var(--wa-color-on-quiet);
- font-size: 1.25em;
-}
-
-::slotted([slot='icon']) {
- margin-inline-end: var(--wa-form-control-padding-inline);
-}
-
-[part~='message'] {
- flex: 1 1 auto;
- display: block;
- overflow: hidden;
-}
diff --git a/packages/webawesome/src/components/callout/callout.styles.ts b/packages/webawesome/src/components/callout/callout.styles.ts
new file mode 100644
index 000000000..9b3b59a54
--- /dev/null
+++ b/packages/webawesome/src/components/callout/callout.styles.ts
@@ -0,0 +1,64 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ display: flex;
+ position: relative;
+ align-items: stretch;
+ border-radius: var(--wa-panel-border-radius);
+ background-color: var(--wa-color-fill-quiet, var(--wa-color-brand-fill-quiet));
+ border-color: var(--wa-color-border-quiet, var(--wa-color-brand-border-quiet));
+ border-style: var(--wa-panel-border-style);
+ border-width: var(--wa-panel-border-width);
+ color: var(--wa-color-text-normal);
+ padding: 1em;
+ }
+
+ /* Appearance modifiers */
+ :host([appearance~='plain']) {
+ background-color: transparent;
+ border-color: transparent;
+ }
+
+ :host([appearance~='outlined']) {
+ background-color: transparent;
+ border-color: var(--wa-color-border-loud, var(--wa-color-brand-border-loud));
+ }
+
+ :host([appearance~='filled']) {
+ background-color: var(--wa-color-fill-quiet, var(--wa-color-brand-fill-quiet));
+ border-color: transparent;
+ }
+
+ :host([appearance~='filled-outlined']) {
+ border-color: var(--wa-color-border-quiet, var(--wa-color-brand-border-quiet));
+ }
+
+ :host([appearance~='accent']) {
+ color: var(--wa-color-on-loud, var(--wa-color-brand-on-loud));
+ background-color: var(--wa-color-fill-loud, var(--wa-color-brand-fill-loud));
+ border-color: transparent;
+
+ [part~='icon'] {
+ color: currentColor;
+ }
+ }
+
+ [part~='icon'] {
+ flex: 0 0 auto;
+ display: flex;
+ align-items: center;
+ color: var(--wa-color-on-quiet);
+ font-size: 1.25em;
+ }
+
+ ::slotted([slot='icon']) {
+ margin-inline-end: var(--wa-form-control-padding-inline);
+ }
+
+ [part~='message'] {
+ flex: 1 1 auto;
+ display: block;
+ overflow: hidden;
+ }
+`;
diff --git a/packages/webawesome/src/components/callout/callout.ts b/packages/webawesome/src/components/callout/callout.ts
index d33f6600b..f8a2a55aa 100644
--- a/packages/webawesome/src/components/callout/callout.ts
+++ b/packages/webawesome/src/components/callout/callout.ts
@@ -1,9 +1,9 @@
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import sizeStyles from '../../styles/utilities/size.css';
-import variantStyles from '../../styles/utilities/variants.css';
-import styles from './callout.css';
+import sizeStyles from '../../styles/component/size.styles.js';
+import variantStyles from '../../styles/component/variants.styles.js';
+import styles from './callout.styles.js';
/**
* @summary Callouts are used to display important messages inline.
diff --git a/packages/webawesome/src/components/card/card.css b/packages/webawesome/src/components/card/card.css
deleted file mode 100644
index cb06f19f6..000000000
--- a/packages/webawesome/src/components/card/card.css
+++ /dev/null
@@ -1,141 +0,0 @@
-:host {
- --spacing: var(--wa-space-l);
-
- /* Internal calculated properties */
- --inner-border-radius: calc(var(--wa-panel-border-radius) - var(--wa-panel-border-width));
-
- display: flex;
- flex-direction: column;
- background-color: var(--wa-color-surface-default);
- border-color: var(--wa-color-surface-border);
- border-radius: var(--wa-panel-border-radius);
- border-style: var(--wa-panel-border-style);
- box-shadow: var(--wa-shadow-s);
- border-width: var(--wa-panel-border-width);
- color: var(--wa-color-text-normal);
-}
-
-/* Appearance modifiers */
-:host([appearance='plain']) {
- background-color: transparent;
- border-color: transparent;
- box-shadow: none;
-}
-
-:host([appearance='outlined']) {
- background-color: var(--wa-color-surface-default);
- border-color: var(--wa-color-surface-border);
-}
-
-:host([appearance='filled']) {
- background-color: var(--wa-color-neutral-fill-quiet);
- border-color: transparent;
-}
-
-:host([appearance='filled-outlined']) {
- background-color: var(--wa-color-neutral-fill-quiet);
- border-color: var(--wa-color-surface-border);
-}
-
-:host([appearance='accent']) {
- color: var(--wa-color-neutral-on-loud);
- background-color: var(--wa-color-neutral-fill-loud);
- border-color: transparent;
-}
-
-/* Take care of top and bottom radii */
-.media,
-:host(:not([with-media])) .header,
-:host(:not([with-media], [with-header])) .body {
- border-start-start-radius: var(--inner-border-radius);
- border-start-end-radius: var(--inner-border-radius);
-}
-
-:host(:not([with-footer])) .body,
-.footer {
- border-end-start-radius: var(--inner-border-radius);
- border-end-end-radius: var(--inner-border-radius);
-}
-
-.media {
- display: flex;
- overflow: hidden;
-
- &::slotted(*) {
- display: block;
- width: 100%;
- border-radius: 0 !important;
- }
-}
-
-/* Round all corners for plain appearance */
-:host([appearance='plain']) .media {
- border-radius: var(--inner-border-radius);
-
- &::slotted(*) {
- border-radius: inherit !important;
- }
-}
-
-.header {
- display: block;
- border-block-end-style: inherit;
- border-block-end-color: var(--wa-color-surface-border);
- border-block-end-width: var(--wa-panel-border-width);
- padding: calc(var(--spacing) / 2) var(--spacing);
-}
-
-.body {
- display: block;
- padding: var(--spacing);
-}
-
-.footer {
- display: block;
- border-block-start-style: inherit;
- border-block-start-color: var(--wa-color-surface-border);
- border-block-start-width: var(--wa-panel-border-width);
- padding: var(--spacing);
-}
-
-/* Push slots to sides when the action slots renders */
-.has-actions {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-:host(:not([with-header])) .header,
-:host(:not([with-footer])) .footer,
-:host(:not([with-media])) .media {
- display: none;
-}
-
-/* Orientation Styles */
-:host([orientation='horizontal']) {
- flex-direction: row;
-
- .media {
- border-start-start-radius: var(--inner-border-radius);
- border-end-start-radius: var(--inner-border-radius);
- border-start-end-radius: 0;
-
- &::slotted(*) {
- block-size: 100%;
- inline-size: 100%;
- object-fit: cover;
- }
- }
-}
-
-:host([orientation='horizontal']) ::slotted([slot='body']) {
- display: block;
- height: 100%;
- margin: 0;
-}
-
-:host([orientation='horizontal']) ::slotted([slot='actions']) {
- display: flex;
- align-items: center;
- padding: var(--spacing);
-}
diff --git a/packages/webawesome/src/components/card/card.styles.ts b/packages/webawesome/src/components/card/card.styles.ts
new file mode 100644
index 000000000..3c8ed1ce0
--- /dev/null
+++ b/packages/webawesome/src/components/card/card.styles.ts
@@ -0,0 +1,145 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --spacing: var(--wa-space-l);
+
+ /* Internal calculated properties */
+ --inner-border-radius: calc(var(--wa-panel-border-radius) - var(--wa-panel-border-width));
+
+ display: flex;
+ flex-direction: column;
+ background-color: var(--wa-color-surface-default);
+ border-color: var(--wa-color-surface-border);
+ border-radius: var(--wa-panel-border-radius);
+ border-style: var(--wa-panel-border-style);
+ box-shadow: var(--wa-shadow-s);
+ border-width: var(--wa-panel-border-width);
+ color: var(--wa-color-text-normal);
+ }
+
+ /* Appearance modifiers */
+ :host([appearance='plain']) {
+ background-color: transparent;
+ border-color: transparent;
+ box-shadow: none;
+ }
+
+ :host([appearance='outlined']) {
+ background-color: var(--wa-color-surface-default);
+ border-color: var(--wa-color-surface-border);
+ }
+
+ :host([appearance='filled']) {
+ background-color: var(--wa-color-neutral-fill-quiet);
+ border-color: transparent;
+ }
+
+ :host([appearance='filled-outlined']) {
+ background-color: var(--wa-color-neutral-fill-quiet);
+ border-color: var(--wa-color-surface-border);
+ }
+
+ :host([appearance='accent']) {
+ color: var(--wa-color-neutral-on-loud);
+ background-color: var(--wa-color-neutral-fill-loud);
+ border-color: transparent;
+ }
+
+ /* Take care of top and bottom radii */
+ .media,
+ :host(:not([with-media])) .header,
+ :host(:not([with-media], [with-header])) .body {
+ border-start-start-radius: var(--inner-border-radius);
+ border-start-end-radius: var(--inner-border-radius);
+ }
+
+ :host(:not([with-footer])) .body,
+ .footer {
+ border-end-start-radius: var(--inner-border-radius);
+ border-end-end-radius: var(--inner-border-radius);
+ }
+
+ .media {
+ display: flex;
+ overflow: hidden;
+
+ &::slotted(*) {
+ display: block;
+ width: 100%;
+ border-radius: 0 !important;
+ }
+ }
+
+ /* Round all corners for plain appearance */
+ :host([appearance='plain']) .media {
+ border-radius: var(--inner-border-radius);
+
+ &::slotted(*) {
+ border-radius: inherit !important;
+ }
+ }
+
+ .header {
+ display: block;
+ border-block-end-style: inherit;
+ border-block-end-color: var(--wa-color-surface-border);
+ border-block-end-width: var(--wa-panel-border-width);
+ padding: calc(var(--spacing) / 2) var(--spacing);
+ }
+
+ .body {
+ display: block;
+ padding: var(--spacing);
+ }
+
+ .footer {
+ display: block;
+ border-block-start-style: inherit;
+ border-block-start-color: var(--wa-color-surface-border);
+ border-block-start-width: var(--wa-panel-border-width);
+ padding: var(--spacing);
+ }
+
+ /* Push slots to sides when the action slots renders */
+ .has-actions {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ :host(:not([with-header])) .header,
+ :host(:not([with-footer])) .footer,
+ :host(:not([with-media])) .media {
+ display: none;
+ }
+
+ /* Orientation Styles */
+ :host([orientation='horizontal']) {
+ flex-direction: row;
+
+ .media {
+ border-start-start-radius: var(--inner-border-radius);
+ border-end-start-radius: var(--inner-border-radius);
+ border-start-end-radius: 0;
+
+ &::slotted(*) {
+ block-size: 100%;
+ inline-size: 100%;
+ object-fit: cover;
+ }
+ }
+ }
+
+ :host([orientation='horizontal']) ::slotted([slot='body']) {
+ display: block;
+ height: 100%;
+ margin: 0;
+ }
+
+ :host([orientation='horizontal']) ::slotted([slot='actions']) {
+ display: flex;
+ align-items: center;
+ padding: var(--spacing);
+ }
+`;
diff --git a/packages/webawesome/src/components/card/card.ts b/packages/webawesome/src/components/card/card.ts
index 58c072852..b4e0ce8e5 100644
--- a/packages/webawesome/src/components/card/card.ts
+++ b/packages/webawesome/src/components/card/card.ts
@@ -2,8 +2,8 @@ import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { HasSlotController } from '../../internal/slot.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import sizeStyles from '../../styles/utilities/size.css';
-import styles from './card.css';
+import sizeStyles from '../../styles/component/size.styles.js';
+import styles from './card.styles.js';
/**
* @summary Cards can be used to group related subjects in a container.
diff --git a/packages/webawesome/src/components/carousel-item/carousel-item.css b/packages/webawesome/src/components/carousel-item/carousel-item.css
deleted file mode 100644
index abc65e317..000000000
--- a/packages/webawesome/src/components/carousel-item/carousel-item.css
+++ /dev/null
@@ -1,19 +0,0 @@
-:host {
- --aspect-ratio: inherit;
-
- display: flex;
- align-items: center;
- justify-content: center;
- flex-direction: column;
- width: 100%;
- max-height: 100%;
- aspect-ratio: var(--aspect-ratio);
- scroll-snap-align: start;
- scroll-snap-stop: always;
-}
-
-::slotted(img) {
- width: 100% !important;
- height: 100% !important;
- object-fit: cover;
-}
diff --git a/packages/webawesome/src/components/carousel-item/carousel-item.styles.ts b/packages/webawesome/src/components/carousel-item/carousel-item.styles.ts
new file mode 100644
index 000000000..11e07af3c
--- /dev/null
+++ b/packages/webawesome/src/components/carousel-item/carousel-item.styles.ts
@@ -0,0 +1,23 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --aspect-ratio: inherit;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ width: 100%;
+ max-height: 100%;
+ aspect-ratio: var(--aspect-ratio);
+ scroll-snap-align: start;
+ scroll-snap-stop: always;
+ }
+
+ ::slotted(img) {
+ width: 100% !important;
+ height: 100% !important;
+ object-fit: cover;
+ }
+`;
diff --git a/packages/webawesome/src/components/carousel-item/carousel-item.ts b/packages/webawesome/src/components/carousel-item/carousel-item.ts
index a655b317c..8b80fd536 100644
--- a/packages/webawesome/src/components/carousel-item/carousel-item.ts
+++ b/packages/webawesome/src/components/carousel-item/carousel-item.ts
@@ -1,7 +1,7 @@
import { html } from 'lit';
import { customElement } from 'lit/decorators.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import styles from './carousel-item.css';
+import styles from './carousel-item.styles.js';
/**
* @summary A carousel item represent a slide within a carousel.
diff --git a/packages/webawesome/src/components/carousel/carousel.css b/packages/webawesome/src/components/carousel/carousel.css
deleted file mode 100644
index 004d30a0a..000000000
--- a/packages/webawesome/src/components/carousel/carousel.css
+++ /dev/null
@@ -1,154 +0,0 @@
-:host {
- --aspect-ratio: 16 / 9;
- --scroll-hint: 0px;
- --slide-gap: var(--wa-space-m, 1rem); /* fallback value is necessary */
-
- display: flex;
-}
-
-.carousel {
- display: grid;
- grid-template-columns: min-content 1fr min-content;
- grid-template-rows: 1fr min-content;
- grid-template-areas:
- '. slides .'
- '. pagination .';
- gap: var(--wa-space-m);
- align-items: center;
- min-height: 100%;
- min-width: 100%;
- position: relative;
-}
-
-.pagination {
- grid-area: pagination;
- display: flex;
- flex-wrap: wrap;
- justify-content: center;
- gap: var(--wa-space-s);
-}
-
-.slides {
- grid-area: slides;
-
- display: grid;
- height: 100%;
- width: 100%;
- align-items: center;
- justify-items: center;
- overflow: auto;
- overscroll-behavior-x: contain;
- scrollbar-width: none;
- aspect-ratio: calc(var(--aspect-ratio) * var(--slides-per-page));
- border-radius: var(--wa-border-radius-m);
-
- --slide-size: calc((100% - (var(--slides-per-page) - 1) * var(--slide-gap)) / var(--slides-per-page));
-}
-
-@media (prefers-reduced-motion) {
- :where(.slides) {
- scroll-behavior: auto;
- }
-}
-
-.slides-horizontal {
- grid-auto-flow: column;
- grid-auto-columns: var(--slide-size);
- grid-auto-rows: 100%;
- column-gap: var(--slide-gap);
- scroll-snap-type: x mandatory;
- scroll-padding-inline: var(--scroll-hint);
- padding-inline: var(--scroll-hint);
- overflow-y: hidden;
-}
-
-.slides-vertical {
- grid-auto-flow: row;
- grid-auto-columns: 100%;
- grid-auto-rows: var(--slide-size);
- row-gap: var(--slide-gap);
- scroll-snap-type: y mandatory;
- scroll-padding-block: var(--scroll-hint);
- padding-block: var(--scroll-hint);
- overflow-x: hidden;
-}
-
-.slides-dragging,
-.slides-dropping {
- scroll-snap-type: unset;
-}
-
-:host([vertical]) ::slotted(wa-carousel-item) {
- height: 100%;
-}
-
-.slides::-webkit-scrollbar {
- display: none;
-}
-
-.navigation {
- grid-area: navigation;
- display: contents;
- font-size: var(--wa-font-size-l);
-}
-
-.navigation-button {
- flex: 0 0 auto;
- display: flex;
- align-items: center;
- background: none;
- border: none;
- border-radius: var(--wa-border-radius-m);
- font-size: inherit;
- color: var(--wa-color-text-quiet);
- padding: var(--wa-space-xs);
- cursor: pointer;
- transition: var(--wa-transition-normal) color;
- appearance: none;
-}
-
-.navigation-button-disabled {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-.navigation-button-disabled::part(base) {
- pointer-events: none;
-}
-
-.navigation-button-previous {
- grid-column: 1;
- grid-row: 1;
-}
-
-.navigation-button-next {
- grid-column: 3;
- grid-row: 1;
-}
-
-.pagination-item {
- display: block;
- cursor: pointer;
- background: none;
- border: 0;
- border-radius: var(--wa-border-radius-circle);
- width: var(--wa-space-s);
- height: var(--wa-space-s);
- background-color: var(--wa-color-neutral-fill-normal);
- padding: 0;
- margin: 0;
- transition: transform var(--wa-transition-slow);
-}
-
-.pagination-item-active {
- background-color: var(--wa-form-control-activated-color);
- transform: scale(1.25);
-}
-
-/* Focus styles */
-.slides:focus-visible,
-.navigation-button:focus-visible,
-.pagination-item:focus-visible {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
-}
diff --git a/packages/webawesome/src/components/carousel/carousel.styles.ts b/packages/webawesome/src/components/carousel/carousel.styles.ts
new file mode 100644
index 000000000..7882b603c
--- /dev/null
+++ b/packages/webawesome/src/components/carousel/carousel.styles.ts
@@ -0,0 +1,158 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --aspect-ratio: 16 / 9;
+ --scroll-hint: 0px;
+ --slide-gap: var(--wa-space-m, 1rem); /* fallback value is necessary */
+
+ display: flex;
+ }
+
+ .carousel {
+ display: grid;
+ grid-template-columns: min-content 1fr min-content;
+ grid-template-rows: 1fr min-content;
+ grid-template-areas:
+ '. slides .'
+ '. pagination .';
+ gap: var(--wa-space-m);
+ align-items: center;
+ min-height: 100%;
+ min-width: 100%;
+ position: relative;
+ }
+
+ .pagination {
+ grid-area: pagination;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: var(--wa-space-s);
+ }
+
+ .slides {
+ grid-area: slides;
+
+ display: grid;
+ height: 100%;
+ width: 100%;
+ align-items: center;
+ justify-items: center;
+ overflow: auto;
+ overscroll-behavior-x: contain;
+ scrollbar-width: none;
+ aspect-ratio: calc(var(--aspect-ratio) * var(--slides-per-page));
+ border-radius: var(--wa-border-radius-m);
+
+ --slide-size: calc((100% - (var(--slides-per-page) - 1) * var(--slide-gap)) / var(--slides-per-page));
+ }
+
+ @media (prefers-reduced-motion) {
+ :where(.slides) {
+ scroll-behavior: auto;
+ }
+ }
+
+ .slides-horizontal {
+ grid-auto-flow: column;
+ grid-auto-columns: var(--slide-size);
+ grid-auto-rows: 100%;
+ column-gap: var(--slide-gap);
+ scroll-snap-type: x mandatory;
+ scroll-padding-inline: var(--scroll-hint);
+ padding-inline: var(--scroll-hint);
+ overflow-y: hidden;
+ }
+
+ .slides-vertical {
+ grid-auto-flow: row;
+ grid-auto-columns: 100%;
+ grid-auto-rows: var(--slide-size);
+ row-gap: var(--slide-gap);
+ scroll-snap-type: y mandatory;
+ scroll-padding-block: var(--scroll-hint);
+ padding-block: var(--scroll-hint);
+ overflow-x: hidden;
+ }
+
+ .slides-dragging,
+ .slides-dropping {
+ scroll-snap-type: unset;
+ }
+
+ :host([vertical]) ::slotted(wa-carousel-item) {
+ height: 100%;
+ }
+
+ .slides::-webkit-scrollbar {
+ display: none;
+ }
+
+ .navigation {
+ grid-area: navigation;
+ display: contents;
+ font-size: var(--wa-font-size-l);
+ }
+
+ .navigation-button {
+ flex: 0 0 auto;
+ display: flex;
+ align-items: center;
+ background: none;
+ border: none;
+ border-radius: var(--wa-border-radius-m);
+ font-size: inherit;
+ color: var(--wa-color-text-quiet);
+ padding: var(--wa-space-xs);
+ cursor: pointer;
+ transition: var(--wa-transition-normal) color;
+ appearance: none;
+ }
+
+ .navigation-button-disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ .navigation-button-disabled::part(base) {
+ pointer-events: none;
+ }
+
+ .navigation-button-previous {
+ grid-column: 1;
+ grid-row: 1;
+ }
+
+ .navigation-button-next {
+ grid-column: 3;
+ grid-row: 1;
+ }
+
+ .pagination-item {
+ display: block;
+ cursor: pointer;
+ background: none;
+ border: 0;
+ border-radius: var(--wa-border-radius-circle);
+ width: var(--wa-space-s);
+ height: var(--wa-space-s);
+ background-color: var(--wa-color-neutral-fill-normal);
+ padding: 0;
+ margin: 0;
+ transition: transform var(--wa-transition-slow);
+ }
+
+ .pagination-item-active {
+ background-color: var(--wa-form-control-activated-color);
+ transform: scale(1.25);
+ }
+
+ /* Focus styles */
+ .slides:focus-visible,
+ .navigation-button:focus-visible,
+ .pagination-item:focus-visible {
+ outline: var(--wa-focus-ring);
+ outline-offset: var(--wa-focus-ring-offset);
+ }
+`;
diff --git a/packages/webawesome/src/components/carousel/carousel.ts b/packages/webawesome/src/components/carousel/carousel.ts
index a3f369dc7..b8353dd95 100644
--- a/packages/webawesome/src/components/carousel/carousel.ts
+++ b/packages/webawesome/src/components/carousel/carousel.ts
@@ -16,7 +16,7 @@ import { LocalizeController } from '../../utilities/localize.js';
import type WaCarouselItem from '../carousel-item/carousel-item.js';
import '../icon/icon.js';
import { AutoplayController } from './autoplay-controller.js';
-import styles from './carousel.css';
+import styles from './carousel.styles.js';
/**
* @summary Carousels display an arbitrary number of content slides along a horizontal or vertical axis.
diff --git a/packages/webawesome/src/components/checkbox/checkbox.css b/packages/webawesome/src/components/checkbox/checkbox.css
deleted file mode 100644
index ae45abbd6..000000000
--- a/packages/webawesome/src/components/checkbox/checkbox.css
+++ /dev/null
@@ -1,100 +0,0 @@
-:host {
- --checked-icon-color: var(--wa-color-brand-on-loud);
- --checked-icon-scale: 0.8;
-
- display: inline-flex;
- color: var(--wa-form-control-value-color);
- font-family: inherit;
- font-weight: var(--wa-form-control-value-font-weight);
- line-height: var(--wa-form-control-value-line-height);
- user-select: none;
- -webkit-user-select: none;
-}
-
-[part~='control'] {
- display: inline-flex;
- flex: 0 0 auto;
- position: relative;
- align-items: center;
- justify-content: center;
- width: var(--wa-form-control-toggle-size);
- height: var(--wa-form-control-toggle-size);
- border-color: var(--wa-form-control-border-color);
- border-radius: min(
- calc(var(--wa-form-control-toggle-size) * 0.375),
- var(--wa-border-radius-s)
- ); /* min prevents entirely circular checkbox */
- border-style: var(--wa-border-style);
- border-width: var(--wa-form-control-border-width);
- background-color: var(--wa-form-control-background-color);
- transition:
- background var(--wa-transition-normal),
- border-color var(--wa-transition-fast),
- box-shadow var(--wa-transition-fast),
- color var(--wa-transition-fast);
- transition-timing-function: var(--wa-transition-easing);
-
- margin-inline-end: 0.5em;
-}
-
-[part~='base'] {
- display: flex;
- align-items: flex-start;
- position: relative;
- color: currentColor;
- vertical-align: middle;
- cursor: pointer;
-}
-
-[part~='label'] {
- display: inline;
-}
-
-/* Checked */
-[part~='control']:has(:checked, :indeterminate) {
- color: var(--checked-icon-color);
- border-color: var(--wa-form-control-activated-color);
- background-color: var(--wa-form-control-activated-color);
-}
-
-/* Focus */
-[part~='control']:has(> input:focus-visible:not(:disabled)) {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
-}
-
-/* Disabled */
-:host [part~='base']:has(input:disabled) {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-input {
- position: absolute;
- padding: 0;
- margin: 0;
- height: 100%;
- width: 100%;
- opacity: 0;
- pointer-events: none;
-}
-
-[part~='icon'] {
- display: flex;
- scale: var(--checked-icon-scale);
-
- /* Without this, Safari renders the icon slightly to the left */
- &::part(svg) {
- translate: 0.0009765625em;
- }
-
- input:not(:checked, :indeterminate) + & {
- visibility: hidden;
- }
-}
-
-:host([required]) [part~='label']::after {
- content: var(--wa-form-control-required-content);
- color: var(--wa-form-control-required-content-color);
- margin-inline-start: var(--wa-form-control-required-content-offset);
-}
diff --git a/packages/webawesome/src/components/checkbox/checkbox.styles.ts b/packages/webawesome/src/components/checkbox/checkbox.styles.ts
new file mode 100644
index 000000000..77fe0c11b
--- /dev/null
+++ b/packages/webawesome/src/components/checkbox/checkbox.styles.ts
@@ -0,0 +1,104 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --checked-icon-color: var(--wa-color-brand-on-loud);
+ --checked-icon-scale: 0.8;
+
+ display: inline-flex;
+ color: var(--wa-form-control-value-color);
+ font-family: inherit;
+ font-weight: var(--wa-form-control-value-font-weight);
+ line-height: var(--wa-form-control-value-line-height);
+ user-select: none;
+ -webkit-user-select: none;
+ }
+
+ [part~='control'] {
+ display: inline-flex;
+ flex: 0 0 auto;
+ position: relative;
+ align-items: center;
+ justify-content: center;
+ width: var(--wa-form-control-toggle-size);
+ height: var(--wa-form-control-toggle-size);
+ border-color: var(--wa-form-control-border-color);
+ border-radius: min(
+ calc(var(--wa-form-control-toggle-size) * 0.375),
+ var(--wa-border-radius-s)
+ ); /* min prevents entirely circular checkbox */
+ border-style: var(--wa-border-style);
+ border-width: var(--wa-form-control-border-width);
+ background-color: var(--wa-form-control-background-color);
+ transition:
+ background var(--wa-transition-normal),
+ border-color var(--wa-transition-fast),
+ box-shadow var(--wa-transition-fast),
+ color var(--wa-transition-fast);
+ transition-timing-function: var(--wa-transition-easing);
+
+ margin-inline-end: 0.5em;
+ }
+
+ [part~='base'] {
+ display: flex;
+ align-items: flex-start;
+ position: relative;
+ color: currentColor;
+ vertical-align: middle;
+ cursor: pointer;
+ }
+
+ [part~='label'] {
+ display: inline;
+ }
+
+ /* Checked */
+ [part~='control']:has(:checked, :indeterminate) {
+ color: var(--checked-icon-color);
+ border-color: var(--wa-form-control-activated-color);
+ background-color: var(--wa-form-control-activated-color);
+ }
+
+ /* Focus */
+ [part~='control']:has(> input:focus-visible:not(:disabled)) {
+ outline: var(--wa-focus-ring);
+ outline-offset: var(--wa-focus-ring-offset);
+ }
+
+ /* Disabled */
+ :host [part~='base']:has(input:disabled) {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ input {
+ position: absolute;
+ padding: 0;
+ margin: 0;
+ height: 100%;
+ width: 100%;
+ opacity: 0;
+ pointer-events: none;
+ }
+
+ [part~='icon'] {
+ display: flex;
+ scale: var(--checked-icon-scale);
+
+ /* Without this, Safari renders the icon slightly to the left */
+ &::part(svg) {
+ translate: 0.0009765625em;
+ }
+
+ input:not(:checked, :indeterminate) + & {
+ visibility: hidden;
+ }
+ }
+
+ :host([required]) [part~='label']::after {
+ content: var(--wa-form-control-required-content);
+ color: var(--wa-form-control-required-content-color);
+ margin-inline-start: var(--wa-form-control-required-content-offset);
+ }
+`;
diff --git a/packages/webawesome/src/components/checkbox/checkbox.ts b/packages/webawesome/src/components/checkbox/checkbox.ts
index 1f1decf62..9af6d20f1 100644
--- a/packages/webawesome/src/components/checkbox/checkbox.ts
+++ b/packages/webawesome/src/components/checkbox/checkbox.ts
@@ -8,10 +8,10 @@ import { HasSlotController } from '../../internal/slot.js';
import { RequiredValidator } from '../../internal/validators/required-validator.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
-import formControlStyles from '../../styles/component/form-control.css';
-import sizeStyles from '../../styles/utilities/size.css';
+import formControlStyles from '../../styles/component/form-control.styles.js';
+import sizeStyles from '../../styles/component/size.styles.js';
import '../icon/icon.js';
-import styles from './checkbox.css';
+import styles from './checkbox.styles.js';
/**
* @summary Checkboxes allow the user to toggle an option on or off.
@@ -108,13 +108,6 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
@property({ type: Boolean, reflect: true, attribute: 'checked' }) defaultChecked: boolean =
this.hasAttribute('checked');
- /**
- * By default, form controls are associated with the nearest containing `
@@ -520,6 +516,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
event.stopPropagation();
if (this.value !== null) {
+ this.selectionOrder.clear();
this.setSelectedOptions([]);
this.displayInput.focus({ preventScroll: true });
@@ -603,24 +600,20 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
if (this.disabled) return;
+ // Mark as interacted so selectionChanged() uses the correct filter logic
+ this.hasInteracted = true;
+ this.valueHasChanged = true;
+
// Use the directly provided option if available (from getTag method)
let option = directOption;
- // If no direct option was provided, find the option from the event path
+ // If no direct option was provided, find the option from the data-value attribute
if (!option) {
- const tagElement = (event.target as Element).closest('wa-tag[part~=tag]');
+ const tagElement = (event.target as Element).closest('wa-tag[data-value]') as HTMLElement | null;
if (tagElement) {
- // Find the index of this tag among all tags
- const tagsContainer = this.shadowRoot?.querySelector('[part="tags"]');
- if (tagsContainer) {
- const allTags = Array.from(tagsContainer.children);
- const index = allTags.indexOf(tagElement as HTMLElement);
-
- if (index >= 0 && index < this.selectedOptions.length) {
- option = this.selectedOptions[index];
- }
- }
+ const value = tagElement.dataset.value;
+ option = this.selectedOptions.find(opt => opt.value === value);
}
}
@@ -707,7 +700,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
const options = this.getAllOptions();
// Update selected options cache
- this.selectedOptions = options.filter(el => {
+ const newSelectedOptions = options.filter(el => {
if (!this.hasInteracted && !this.valueHasChanged) {
const defaultValue = this.defaultValue;
const defaultValues = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
@@ -717,6 +710,32 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
return el.selected;
});
+ // Update the selection order map
+ const newSelectedValues = new Set(newSelectedOptions.map(el => el.value));
+
+ // Remove deselected options from the order map
+ for (const value of this.selectionOrder.keys()) {
+ if (!newSelectedValues.has(value)) {
+ this.selectionOrder.delete(value);
+ }
+ }
+
+ // Add newly selected options
+ const maxOrder = this.selectionOrder.size > 0 ? Math.max(...this.selectionOrder.values()) : -1;
+ let nextOrder = maxOrder + 1;
+ for (const option of newSelectedOptions) {
+ if (!this.selectionOrder.has(option.value)) {
+ this.selectionOrder.set(option.value, nextOrder++);
+ }
+ }
+
+ // Sort options by selection order
+ this.selectedOptions = newSelectedOptions.sort((a, b) => {
+ const orderA = this.selectionOrder.get(a.value) ?? 0;
+ const orderB = this.selectionOrder.get(b.value) ?? 0;
+ return orderA - orderB;
+ });
+
let selectedValues = new Set(this.selectedOptions.map(el => el.value));
// Toggle values present in the DOM from this.value, while preserving options NOT present in the DOM (for lazy loading)
@@ -888,6 +907,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
}
formResetCallback() {
+ this.selectionOrder.clear();
this.value = this.defaultValue;
super.formResetCallback();
this.handleValueChange();
diff --git a/packages/webawesome/src/components/skeleton/skeleton.css b/packages/webawesome/src/components/skeleton/skeleton.css
deleted file mode 100644
index 65a7f794a..000000000
--- a/packages/webawesome/src/components/skeleton/skeleton.css
+++ /dev/null
@@ -1,54 +0,0 @@
-:host {
- --color: var(--wa-color-neutral-fill-normal);
- --sheen-color: color-mix(in oklab, var(--color), var(--wa-color-surface-raised));
-
- display: flex;
- position: relative;
- width: 100%;
- height: 100%;
- min-height: 1rem;
-}
-
-.indicator {
- flex: 1 1 auto;
- background: var(--color);
- border-radius: var(--wa-border-radius-pill);
-}
-
-:host([effect='sheen']) .indicator {
- background: linear-gradient(270deg, var(--sheen-color), var(--color), var(--color), var(--sheen-color));
- background-size: 400% 100%;
- animation: sheen 8s ease-in-out infinite;
-}
-
-:host([effect='pulse']) .indicator {
- animation: pulse 2s ease-in-out 0.5s infinite;
-}
-
-/* Forced colors mode */
-@media (forced-colors: active) {
- :host {
- --color: GrayText;
- }
-}
-
-@keyframes sheen {
- 0% {
- background-position: 200% 0;
- }
- to {
- background-position: -200% 0;
- }
-}
-
-@keyframes pulse {
- 0% {
- opacity: 1;
- }
- 50% {
- opacity: 0.4;
- }
- 100% {
- opacity: 1;
- }
-}
diff --git a/packages/webawesome/src/components/skeleton/skeleton.styles.ts b/packages/webawesome/src/components/skeleton/skeleton.styles.ts
new file mode 100644
index 000000000..9fd766ede
--- /dev/null
+++ b/packages/webawesome/src/components/skeleton/skeleton.styles.ts
@@ -0,0 +1,58 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --color: var(--wa-color-neutral-fill-normal);
+ --sheen-color: color-mix(in oklab, var(--color), var(--wa-color-surface-raised));
+
+ display: flex;
+ position: relative;
+ width: 100%;
+ height: 100%;
+ min-height: 1rem;
+ }
+
+ .indicator {
+ flex: 1 1 auto;
+ background: var(--color);
+ border-radius: var(--wa-border-radius-pill);
+ }
+
+ :host([effect='sheen']) .indicator {
+ background: linear-gradient(270deg, var(--sheen-color), var(--color), var(--color), var(--sheen-color));
+ background-size: 400% 100%;
+ animation: sheen 8s ease-in-out infinite;
+ }
+
+ :host([effect='pulse']) .indicator {
+ animation: pulse 2s ease-in-out 0.5s infinite;
+ }
+
+ /* Forced colors mode */
+ @media (forced-colors: active) {
+ :host {
+ --color: GrayText;
+ }
+ }
+
+ @keyframes sheen {
+ 0% {
+ background-position: 200% 0;
+ }
+ to {
+ background-position: -200% 0;
+ }
+ }
+
+ @keyframes pulse {
+ 0% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.4;
+ }
+ 100% {
+ opacity: 1;
+ }
+ }
+`;
diff --git a/packages/webawesome/src/components/skeleton/skeleton.ts b/packages/webawesome/src/components/skeleton/skeleton.ts
index b22178fd9..2b4723d77 100644
--- a/packages/webawesome/src/components/skeleton/skeleton.ts
+++ b/packages/webawesome/src/components/skeleton/skeleton.ts
@@ -1,7 +1,7 @@
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import styles from './skeleton.css';
+import styles from './skeleton.styles.js';
/**
* @summary Skeletons are used to provide a visual representation of where content will eventually be drawn.
diff --git a/packages/webawesome/src/components/slider/slider.css b/packages/webawesome/src/components/slider/slider.css
deleted file mode 100644
index efb206130..000000000
--- a/packages/webawesome/src/components/slider/slider.css
+++ /dev/null
@@ -1,229 +0,0 @@
-:host {
- --track-size: 0.5em;
- --thumb-width: 1.4em;
- --thumb-height: 1.4em;
- --marker-width: 0.1875em;
- --marker-height: 0.1875em;
-}
-
-:host([orientation='vertical']) {
- width: auto;
-}
-
-#label:has(~ .vertical) {
- display: block;
- order: 2;
- max-width: none;
- text-align: center;
-}
-
-#description:has(~ .vertical) {
- order: 3;
- text-align: center;
-}
-
-/* Add extra space between slider and label, when present */
-#label:has(*:not(:empty)) ~ #slider {
- &.horizontal {
- margin-block-start: 0.5em;
- }
- &.vertical {
- margin-block-end: 0.5em;
- }
-}
-
-#slider {
- touch-action: none;
-
- &:focus {
- outline: none;
- }
-
- &:focus-visible:not(.disabled) #thumb,
- &:focus-visible:not(.disabled) #thumb-min,
- &:focus-visible:not(.disabled) #thumb-max {
- outline: var(--wa-focus-ring);
- /* intentionally no offset due to border */
- }
-}
-
-#track {
- position: relative;
- border-radius: 9999px;
- background: var(--wa-color-neutral-fill-normal);
- isolation: isolate;
-}
-
-/* Orientation */
-.horizontal #track {
- height: var(--track-size);
-}
-
-.vertical #track {
- order: 1;
- width: var(--track-size);
- height: 200px;
-}
-
-/* Disabled */
-.disabled #track {
- cursor: not-allowed;
- opacity: 0.5;
-}
-
-/* Indicator */
-#indicator {
- position: absolute;
- border-radius: inherit;
- background-color: var(--wa-form-control-activated-color);
-
- &:dir(ltr) {
- right: calc(100% - max(var(--start), var(--end)));
- left: min(var(--start), var(--end));
- }
-
- &:dir(rtl) {
- right: min(var(--start), var(--end));
- left: calc(100% - max(var(--start), var(--end)));
- }
-}
-
-.horizontal #indicator {
- top: 0;
- height: 100%;
-}
-
-.vertical #indicator {
- top: calc(100% - var(--end));
- bottom: var(--start);
- left: 0;
- width: 100%;
-}
-
-/* Thumbs */
-#thumb,
-#thumb-min,
-#thumb-max {
- z-index: 3;
- position: absolute;
- width: var(--thumb-width);
- height: var(--thumb-height);
- border: solid 0.125em var(--wa-color-surface-default);
- border-radius: 50%;
- background-color: var(--wa-form-control-activated-color);
- cursor: pointer;
-}
-
-.disabled #thumb,
-.disabled #thumb-min,
-.disabled #thumb-max {
- cursor: inherit;
-}
-
-.horizontal #thumb,
-.horizontal #thumb-min,
-.horizontal #thumb-max {
- top: calc(50% - var(--thumb-height) / 2);
-
- &:dir(ltr) {
- right: auto;
- left: calc(var(--position) - var(--thumb-width) / 2);
- }
-
- &:dir(rtl) {
- right: calc(var(--position) - var(--thumb-width) / 2);
- left: auto;
- }
-}
-
-.vertical #thumb,
-.vertical #thumb-min,
-.vertical #thumb-max {
- bottom: calc(var(--position) - var(--thumb-height) / 2);
- left: calc(50% - var(--thumb-width) / 2);
-}
-
-/* Range-specific thumb styles */
-:host([range]) {
- #thumb-min:focus-visible,
- #thumb-max:focus-visible {
- z-index: 4; /* Ensure focused thumb appears on top */
- outline: var(--wa-focus-ring);
- /* intentionally no offset due to border */
- }
-}
-
-/* Markers */
-#markers {
- pointer-events: none;
-}
-
-.marker {
- z-index: 2;
- position: absolute;
- width: var(--marker-width);
- height: var(--marker-height);
- border-radius: 50%;
- background-color: var(--wa-color-surface-default);
-}
-
-.marker:first-of-type,
-.marker:last-of-type {
- display: none;
-}
-
-.horizontal .marker {
- top: calc(50% - var(--marker-height) / 2);
- left: calc(var(--position) - var(--marker-width) / 2);
-}
-
-.vertical .marker {
- top: calc(var(--position) - var(--marker-height) / 2);
- left: calc(50% - var(--marker-width) / 2);
-}
-
-/* Marker labels */
-#references {
- position: relative;
-
- slot {
- display: flex;
- justify-content: space-between;
- height: 100%;
- }
-
- ::slotted(*) {
- color: var(--wa-color-text-quiet);
- font-size: 0.875em;
- line-height: 1;
- }
-}
-
-.horizontal {
- #references {
- margin-block-start: 0.5em;
- }
-}
-
-.vertical {
- display: flex;
- margin-inline: auto;
-
- #track {
- order: 1;
- }
-
- #references {
- order: 2;
- width: min-content;
- margin-inline-start: 0.75em;
-
- slot {
- flex-direction: column;
- }
- }
-}
-
-.vertical #references slot {
- flex-direction: column;
-}
diff --git a/packages/webawesome/src/components/slider/slider.styles.ts b/packages/webawesome/src/components/slider/slider.styles.ts
new file mode 100644
index 000000000..5e3b5a6c7
--- /dev/null
+++ b/packages/webawesome/src/components/slider/slider.styles.ts
@@ -0,0 +1,233 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --track-size: 0.5em;
+ --thumb-width: 1.4em;
+ --thumb-height: 1.4em;
+ --marker-width: 0.1875em;
+ --marker-height: 0.1875em;
+ }
+
+ :host([orientation='vertical']) {
+ width: auto;
+ }
+
+ #label:has(~ .vertical) {
+ display: block;
+ order: 2;
+ max-width: none;
+ text-align: center;
+ }
+
+ #description:has(~ .vertical) {
+ order: 3;
+ text-align: center;
+ }
+
+ /* Add extra space between slider and label, when present */
+ #label:has(*:not(:empty)) ~ #slider {
+ &.horizontal {
+ margin-block-start: 0.5em;
+ }
+ &.vertical {
+ margin-block-end: 0.5em;
+ }
+ }
+
+ #slider {
+ touch-action: none;
+
+ &:focus {
+ outline: none;
+ }
+
+ &:focus-visible:not(.disabled) #thumb,
+ &:focus-visible:not(.disabled) #thumb-min,
+ &:focus-visible:not(.disabled) #thumb-max {
+ outline: var(--wa-focus-ring);
+ /* intentionally no offset due to border */
+ }
+ }
+
+ #track {
+ position: relative;
+ border-radius: 9999px;
+ background: var(--wa-color-neutral-fill-normal);
+ isolation: isolate;
+ }
+
+ /* Orientation */
+ .horizontal #track {
+ height: var(--track-size);
+ }
+
+ .vertical #track {
+ order: 1;
+ width: var(--track-size);
+ height: 200px;
+ }
+
+ /* Disabled */
+ .disabled #track {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+
+ /* Indicator */
+ #indicator {
+ position: absolute;
+ border-radius: inherit;
+ background-color: var(--wa-form-control-activated-color);
+
+ &:dir(ltr) {
+ right: calc(100% - max(var(--start), var(--end)));
+ left: min(var(--start), var(--end));
+ }
+
+ &:dir(rtl) {
+ right: min(var(--start), var(--end));
+ left: calc(100% - max(var(--start), var(--end)));
+ }
+ }
+
+ .horizontal #indicator {
+ top: 0;
+ height: 100%;
+ }
+
+ .vertical #indicator {
+ top: calc(100% - var(--end));
+ bottom: var(--start);
+ left: 0;
+ width: 100%;
+ }
+
+ /* Thumbs */
+ #thumb,
+ #thumb-min,
+ #thumb-max {
+ z-index: 3;
+ position: absolute;
+ width: var(--thumb-width);
+ height: var(--thumb-height);
+ border: solid 0.125em var(--wa-color-surface-default);
+ border-radius: 50%;
+ background-color: var(--wa-form-control-activated-color);
+ cursor: pointer;
+ }
+
+ .disabled #thumb,
+ .disabled #thumb-min,
+ .disabled #thumb-max {
+ cursor: inherit;
+ }
+
+ .horizontal #thumb,
+ .horizontal #thumb-min,
+ .horizontal #thumb-max {
+ top: calc(50% - var(--thumb-height) / 2);
+
+ &:dir(ltr) {
+ right: auto;
+ left: calc(var(--position) - var(--thumb-width) / 2);
+ }
+
+ &:dir(rtl) {
+ right: calc(var(--position) - var(--thumb-width) / 2);
+ left: auto;
+ }
+ }
+
+ .vertical #thumb,
+ .vertical #thumb-min,
+ .vertical #thumb-max {
+ bottom: calc(var(--position) - var(--thumb-height) / 2);
+ left: calc(50% - var(--thumb-width) / 2);
+ }
+
+ /* Range-specific thumb styles */
+ :host([range]) {
+ #thumb-min:focus-visible,
+ #thumb-max:focus-visible {
+ z-index: 4; /* Ensure focused thumb appears on top */
+ outline: var(--wa-focus-ring);
+ /* intentionally no offset due to border */
+ }
+ }
+
+ /* Markers */
+ #markers {
+ pointer-events: none;
+ }
+
+ .marker {
+ z-index: 2;
+ position: absolute;
+ width: var(--marker-width);
+ height: var(--marker-height);
+ border-radius: 50%;
+ background-color: var(--wa-color-surface-default);
+ }
+
+ .marker:first-of-type,
+ .marker:last-of-type {
+ display: none;
+ }
+
+ .horizontal .marker {
+ top: calc(50% - var(--marker-height) / 2);
+ left: calc(var(--position) - var(--marker-width) / 2);
+ }
+
+ .vertical .marker {
+ top: calc(var(--position) - var(--marker-height) / 2);
+ left: calc(50% - var(--marker-width) / 2);
+ }
+
+ /* Marker labels */
+ #references {
+ position: relative;
+
+ slot {
+ display: flex;
+ justify-content: space-between;
+ height: 100%;
+ }
+
+ ::slotted(*) {
+ color: var(--wa-color-text-quiet);
+ font-size: 0.875em;
+ line-height: 1;
+ }
+ }
+
+ .horizontal {
+ #references {
+ margin-block-start: 0.5em;
+ }
+ }
+
+ .vertical {
+ display: flex;
+ margin-inline: auto;
+
+ #track {
+ order: 1;
+ }
+
+ #references {
+ order: 2;
+ width: min-content;
+ margin-inline-start: 0.75em;
+
+ slot {
+ flex-direction: column;
+ }
+ }
+ }
+
+ .vertical #references slot {
+ flex-direction: column;
+ }
+`;
diff --git a/packages/webawesome/src/components/slider/slider.ts b/packages/webawesome/src/components/slider/slider.ts
index 4a4817aad..dab5b6eb7 100644
--- a/packages/webawesome/src/components/slider/slider.ts
+++ b/packages/webawesome/src/components/slider/slider.ts
@@ -8,12 +8,12 @@ import { HasSlotController } from '../../internal/slot.js';
import { submitOnEnter } from '../../internal/submit-on-enter.js';
import { SliderValidator } from '../../internal/validators/slider-validator.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
-import formControlStyles from '../../styles/component/form-control.css';
-import sizeStyles from '../../styles/utilities/size.css';
+import formControlStyles from '../../styles/component/form-control.styles.js';
+import sizeStyles from '../../styles/component/size.styles.js';
import { LocalizeController } from '../../utilities/localize.js';
import '../tooltip/tooltip.js';
import type WaTooltip from '../tooltip/tooltip.js';
-import styles from './slider.css';
+import styles from './slider.styles.js';
/**
*
@@ -167,12 +167,6 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
/** The starting value from which to draw the slider's fill, which is based on its current value. */
@property({ attribute: 'indicator-offset', type: Number }) indicatorOffset: number;
- /**
- * The form to associate this control with. If omitted, the closest containing `` will be used. The value of
- * this attribute must be an ID of a form in the same document or shadow root.
- */
- @property({ reflect: true }) form = null;
-
/** The minimum value allowed. */
@property({ type: Number }) min: number = 0;
@@ -437,8 +431,14 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
/** Clamps a number to min/max while ensuring it's a valid step interval. */
private clampAndRoundToStep(value: number) {
const stepPrecision = (String(this.step).split('.')[1] || '').replace(/0+$/g, '').length;
- value = Math.round(value / this.step) * this.step;
- value = clamp(value, this.min, this.max);
+
+ // Ensure we're working with numbers (in case the user passes strings to the respective properties)
+ const step = Number(this.step);
+ const min = Number(this.min);
+ const max = Number(this.max);
+
+ value = Math.round(value / step) * step;
+ value = clamp(value, min, max);
return parseFloat(value.toFixed(stepPrecision));
}
diff --git a/packages/webawesome/src/components/spinner/spinner.css b/packages/webawesome/src/components/spinner/spinner.css
deleted file mode 100644
index cc28a04c8..000000000
--- a/packages/webawesome/src/components/spinner/spinner.css
+++ /dev/null
@@ -1,59 +0,0 @@
-:host {
- --track-width: 2px;
- --track-color: var(--wa-color-neutral-fill-normal);
- --indicator-color: var(--wa-color-brand-fill-loud);
- --speed: 2s;
-
- /* Resizing a spinner element using anything but font-size will break the animation because the animation uses em units.
- Therefore, if a spinner is used in a flex container without `flex: none` applied, the spinner can grow/shrink and
- break the animation. The use of `flex: none` on the host element prevents this by always having the spinner sized
- according to its actual dimensions.
- */
- flex: none;
- display: inline-flex;
- width: 1em;
- height: 1em;
-}
-
-svg {
- width: 100%;
- height: 100%;
- aspect-ratio: 1;
- animation: spin var(--speed) linear infinite;
-}
-
-.track {
- stroke: var(--track-color);
-}
-
-.indicator {
- stroke: var(--indicator-color);
- stroke-dasharray: 75, 100;
- stroke-dashoffset: -5;
- animation: dash 1.5s ease-in-out infinite;
- stroke-linecap: round;
-}
-
-@keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
-}
-
-@keyframes dash {
- 0% {
- stroke-dasharray: 1, 150;
- stroke-dashoffset: 0;
- }
- 50% {
- stroke-dasharray: 90, 150;
- stroke-dashoffset: -35;
- }
- 100% {
- stroke-dasharray: 90, 150;
- stroke-dashoffset: -124;
- }
-}
diff --git a/packages/webawesome/src/components/spinner/spinner.styles.ts b/packages/webawesome/src/components/spinner/spinner.styles.ts
new file mode 100644
index 000000000..f965c6fd9
--- /dev/null
+++ b/packages/webawesome/src/components/spinner/spinner.styles.ts
@@ -0,0 +1,64 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --track-width: 2px;
+ --track-color: var(--wa-color-neutral-fill-normal);
+ --indicator-color: var(--wa-color-brand-fill-loud);
+ --speed: 2s;
+
+ /*
+ Resizing a spinner element using anything but font-size will break the animation because the animation uses em
+ units. Therefore, if a spinner is used in a flex container without \`flex: none\` applied, the spinner can
+ grow/shrink and break the animation. The use of \`flex: none\` on the host element prevents this by always having
+ the spinner sized according to its actual dimensions.
+ */
+ flex: none;
+ display: inline-flex;
+ width: 1em;
+ height: 1em;
+ }
+
+ svg {
+ width: 100%;
+ height: 100%;
+ aspect-ratio: 1;
+ animation: spin var(--speed) linear infinite;
+ }
+
+ .track {
+ stroke: var(--track-color);
+ }
+
+ .indicator {
+ stroke: var(--indicator-color);
+ stroke-dasharray: 75, 100;
+ stroke-dashoffset: -5;
+ animation: dash 1.5s ease-in-out infinite;
+ stroke-linecap: round;
+ }
+
+ @keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+ }
+
+ @keyframes dash {
+ 0% {
+ stroke-dasharray: 1, 150;
+ stroke-dashoffset: 0;
+ }
+ 50% {
+ stroke-dasharray: 90, 150;
+ stroke-dashoffset: -35;
+ }
+ 100% {
+ stroke-dasharray: 90, 150;
+ stroke-dashoffset: -124;
+ }
+ }
+`;
diff --git a/packages/webawesome/src/components/spinner/spinner.ts b/packages/webawesome/src/components/spinner/spinner.ts
index ca3d2b45d..9985a492c 100644
--- a/packages/webawesome/src/components/spinner/spinner.ts
+++ b/packages/webawesome/src/components/spinner/spinner.ts
@@ -2,7 +2,7 @@ import { html } from 'lit';
import { customElement } from 'lit/decorators.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
-import styles from './spinner.css';
+import styles from './spinner.styles.js';
/**
* @summary Spinners are used to show the progress of an indeterminate operation.
diff --git a/packages/webawesome/src/components/split-panel/split-panel.css b/packages/webawesome/src/components/split-panel/split-panel.css
deleted file mode 100644
index 4c9187b7e..000000000
--- a/packages/webawesome/src/components/split-panel/split-panel.css
+++ /dev/null
@@ -1,73 +0,0 @@
-:host {
- --divider-width: 0.25rem;
- --divider-hit-area: 0.75rem;
- --min: 0%;
- --max: 100%;
-
- display: grid;
-}
-
-.start,
-.end {
- overflow: hidden;
-}
-
-.divider {
- flex: 0 0 var(--divider-width);
- display: flex;
- position: relative;
- align-items: center;
- justify-content: center;
- background-color: var(--wa-color-neutral-border-normal);
- color: var(--wa-color-neutral-on-normal);
- z-index: 1;
-}
-
-.divider:focus {
- outline: none;
-}
-
-:host(:not([disabled])) .divider:focus-visible {
- outline: var(--wa-focus-ring);
-}
-
-:host([disabled]) .divider {
- cursor: not-allowed;
-}
-
-/* Horizontal */
-:host(:not([orientation='vertical'], [disabled])) .divider {
- cursor: col-resize;
-}
-
-:host(:not([orientation='vertical'])) .divider::after {
- display: flex;
- content: '';
- position: absolute;
- height: 100%;
- left: calc(var(--divider-hit-area) / -2 + var(--divider-width) / 2);
- width: var(--divider-hit-area);
-}
-
-/* Vertical */
-:host([orientation='vertical']) {
- flex-direction: column;
-}
-
-:host([orientation='vertical']:not([disabled])) .divider {
- cursor: row-resize;
-}
-
-:host([orientation='vertical']) .divider::after {
- content: '';
- position: absolute;
- width: 100%;
- top: calc(var(--divider-hit-area) / -2 + var(--divider-width) / 2);
- height: var(--divider-hit-area);
-}
-
-@media (forced-colors: active) {
- .divider {
- outline: solid 1px transparent;
- }
-}
diff --git a/packages/webawesome/src/components/split-panel/split-panel.styles.ts b/packages/webawesome/src/components/split-panel/split-panel.styles.ts
new file mode 100644
index 000000000..aada89211
--- /dev/null
+++ b/packages/webawesome/src/components/split-panel/split-panel.styles.ts
@@ -0,0 +1,77 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --divider-width: 0.25rem;
+ --divider-hit-area: 0.75rem;
+ --min: 0%;
+ --max: 100%;
+
+ display: grid;
+ }
+
+ .start,
+ .end {
+ overflow: hidden;
+ }
+
+ .divider {
+ flex: 0 0 var(--divider-width);
+ display: flex;
+ position: relative;
+ align-items: center;
+ justify-content: center;
+ background-color: var(--wa-color-neutral-border-normal);
+ color: var(--wa-color-neutral-on-normal);
+ z-index: 1;
+ }
+
+ .divider:focus {
+ outline: none;
+ }
+
+ :host(:not([disabled])) .divider:focus-visible {
+ outline: var(--wa-focus-ring);
+ }
+
+ :host([disabled]) .divider {
+ cursor: not-allowed;
+ }
+
+ /* Horizontal */
+ :host(:not([orientation='vertical'], [disabled])) .divider {
+ cursor: col-resize;
+ }
+
+ :host(:not([orientation='vertical'])) .divider::after {
+ display: flex;
+ content: '';
+ position: absolute;
+ height: 100%;
+ left: calc(var(--divider-hit-area) / -2 + var(--divider-width) / 2);
+ width: var(--divider-hit-area);
+ }
+
+ /* Vertical */
+ :host([orientation='vertical']) {
+ flex-direction: column;
+ }
+
+ :host([orientation='vertical']:not([disabled])) .divider {
+ cursor: row-resize;
+ }
+
+ :host([orientation='vertical']) .divider::after {
+ content: '';
+ position: absolute;
+ width: 100%;
+ top: calc(var(--divider-hit-area) / -2 + var(--divider-width) / 2);
+ height: var(--divider-hit-area);
+ }
+
+ @media (forced-colors: active) {
+ .divider {
+ outline: solid 1px transparent;
+ }
+ }
+`;
diff --git a/packages/webawesome/src/components/split-panel/split-panel.ts b/packages/webawesome/src/components/split-panel/split-panel.ts
index 7e83bb90b..e3930db79 100644
--- a/packages/webawesome/src/components/split-panel/split-panel.ts
+++ b/packages/webawesome/src/components/split-panel/split-panel.ts
@@ -7,7 +7,7 @@ import { clamp } from '../../internal/math.js';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
-import styles from './split-panel.css';
+import styles from './split-panel.styles.js';
/**
* @summary Split panels display two adjacent panels, allowing the user to reposition them.
diff --git a/packages/webawesome/src/components/switch/switch.css b/packages/webawesome/src/components/switch/switch.css
deleted file mode 100644
index 941bf017c..000000000
--- a/packages/webawesome/src/components/switch/switch.css
+++ /dev/null
@@ -1,98 +0,0 @@
-:host {
- --height: var(--wa-form-control-toggle-size);
- --width: calc(var(--height) * 1.75);
- --thumb-size: 0.75em;
-
- display: inline-flex;
- line-height: var(--wa-form-control-value-line-height);
-}
-
-label {
- position: relative;
- display: flex;
- align-items: center;
- font: inherit;
- color: var(--wa-form-control-value-color);
- vertical-align: middle;
- cursor: pointer;
-}
-
-.switch {
- flex: 0 0 auto;
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- width: var(--width);
- height: var(--height);
- background-color: var(--wa-form-control-background-color);
- border-color: var(--wa-form-control-border-color);
- border-radius: var(--height);
- border-style: var(--wa-form-control-border-style);
- border-width: var(--wa-form-control-border-width);
- transition-property: translate, background, border-color, box-shadow;
- transition-duration: var(--wa-transition-normal);
- transition-timing-function: var(--wa-transition-easing);
-}
-
-.switch .thumb {
- aspect-ratio: 1 / 1;
- width: var(--thumb-size);
- height: var(--thumb-size);
- background-color: var(--wa-form-control-border-color);
- border-radius: 50%;
- translate: calc((var(--width) - var(--height)) / -2);
- transition: inherit;
-}
-
-.input {
- position: absolute;
- opacity: 0;
- padding: 0;
- margin: 0;
- pointer-events: none;
-}
-
-/* Focus */
-label:not(.disabled) .input:focus-visible ~ .switch .thumb {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
-}
-
-/* Checked */
-.checked .switch {
- background-color: var(--wa-form-control-activated-color);
- border-color: var(--wa-form-control-activated-color);
-}
-
-.checked .switch .thumb {
- background-color: var(--wa-color-surface-default);
- translate: calc((var(--width) - var(--height)) / 2);
-}
-
-/* Disabled */
-label:has(> :disabled) {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-[part~='label'] {
- display: inline-block;
- line-height: var(--height);
- margin-inline-start: 0.5em;
- user-select: none;
- -webkit-user-select: none;
-}
-
-:host([required]) [part~='label']::after {
- content: var(--wa-form-control-required-content);
- color: var(--wa-form-control-required-content-color);
- margin-inline-start: var(--wa-form-control-required-content-offset);
-}
-
-@media (forced-colors: active) {
- :checked:enabled + .switch:hover .thumb,
- :checked + .switch .thumb {
- background-color: ButtonText;
- }
-}
diff --git a/packages/webawesome/src/components/switch/switch.styles.ts b/packages/webawesome/src/components/switch/switch.styles.ts
new file mode 100644
index 000000000..f1b689e57
--- /dev/null
+++ b/packages/webawesome/src/components/switch/switch.styles.ts
@@ -0,0 +1,102 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --height: var(--wa-form-control-toggle-size);
+ --width: calc(var(--height) * 1.75);
+ --thumb-size: 0.75em;
+
+ display: inline-flex;
+ line-height: var(--wa-form-control-value-line-height);
+ }
+
+ label {
+ position: relative;
+ display: flex;
+ align-items: center;
+ font: inherit;
+ color: var(--wa-form-control-value-color);
+ vertical-align: middle;
+ cursor: pointer;
+ }
+
+ .switch {
+ flex: 0 0 auto;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: var(--width);
+ height: var(--height);
+ background-color: var(--wa-form-control-background-color);
+ border-color: var(--wa-form-control-border-color);
+ border-radius: var(--height);
+ border-style: var(--wa-form-control-border-style);
+ border-width: var(--wa-form-control-border-width);
+ transition-property: translate, background, border-color, box-shadow;
+ transition-duration: var(--wa-transition-normal);
+ transition-timing-function: var(--wa-transition-easing);
+ }
+
+ .switch .thumb {
+ aspect-ratio: 1 / 1;
+ width: var(--thumb-size);
+ height: var(--thumb-size);
+ background-color: var(--wa-form-control-border-color);
+ border-radius: 50%;
+ translate: calc((var(--width) - var(--height)) / -2);
+ transition: inherit;
+ }
+
+ .input {
+ position: absolute;
+ opacity: 0;
+ padding: 0;
+ margin: 0;
+ pointer-events: none;
+ }
+
+ /* Focus */
+ label:not(.disabled) .input:focus-visible ~ .switch .thumb {
+ outline: var(--wa-focus-ring);
+ outline-offset: var(--wa-focus-ring-offset);
+ }
+
+ /* Checked */
+ .checked .switch {
+ background-color: var(--wa-form-control-activated-color);
+ border-color: var(--wa-form-control-activated-color);
+ }
+
+ .checked .switch .thumb {
+ background-color: var(--wa-color-surface-default);
+ translate: calc((var(--width) - var(--height)) / 2);
+ }
+
+ /* Disabled */
+ label:has(> :disabled) {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ [part~='label'] {
+ display: inline-block;
+ line-height: var(--height);
+ margin-inline-start: 0.5em;
+ user-select: none;
+ -webkit-user-select: none;
+ }
+
+ :host([required]) [part~='label']::after {
+ content: var(--wa-form-control-required-content);
+ color: var(--wa-form-control-required-content-color);
+ margin-inline-start: var(--wa-form-control-required-content-offset);
+ }
+
+ @media (forced-colors: active) {
+ :checked:enabled + .switch:hover .thumb,
+ :checked + .switch .thumb {
+ background-color: ButtonText;
+ }
+ }
+`;
diff --git a/packages/webawesome/src/components/switch/switch.ts b/packages/webawesome/src/components/switch/switch.ts
index a78807122..99e3b8de0 100644
--- a/packages/webawesome/src/components/switch/switch.ts
+++ b/packages/webawesome/src/components/switch/switch.ts
@@ -8,9 +8,9 @@ import { HasSlotController } from '../../internal/slot.js';
import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
-import formControlStyles from '../../styles/component/form-control.css';
-import sizeStyles from '../../styles/utilities/size.css';
-import styles from './switch.css';
+import formControlStyles from '../../styles/component/form-control.styles.js';
+import sizeStyles from '../../styles/component/size.styles.js';
+import styles from './switch.styles.js';
/**
* @summary Switches allow the user to toggle an option on or off.
@@ -80,13 +80,6 @@ export default class WaSwitch extends WebAwesomeFormAssociatedElement {
@property({ type: Boolean, attribute: 'checked', reflect: true }) defaultChecked: boolean =
this.hasAttribute('checked');
- /**
- * By default, form controls are associated with the nearest containing ` ` element. This attribute allows you
- * to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
- * the same document or shadow root for this to work.
- */
- @property({ reflect: true }) form = null;
-
/** Makes the switch a required field. */
@property({ type: Boolean, reflect: true }) required = false;
diff --git a/packages/webawesome/src/components/tab-group/tab-group.css b/packages/webawesome/src/components/tab-group/tab-group.css
deleted file mode 100644
index 6606fa163..000000000
--- a/packages/webawesome/src/components/tab-group/tab-group.css
+++ /dev/null
@@ -1,224 +0,0 @@
-:host {
- --indicator-color: var(--wa-color-brand-fill-loud);
- --track-color: var(--wa-color-neutral-fill-normal);
- --track-width: 0.125rem;
-
- display: block;
-}
-
-.tab-group {
- display: flex;
- border-radius: 0;
-}
-
-.tabs {
- display: flex;
- position: relative;
-}
-
-.indicator {
- position: absolute;
-}
-
-.tab-group-has-scroll-controls .nav-container {
- position: relative;
- padding: 0 1.5em;
-}
-
-.body {
- display: block;
-}
-
-.scroll-button {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- top: 0;
- bottom: 0;
- width: 1.5em;
-}
-
-.scroll-button-start {
- inset-inline-start: 0;
-}
-
-.scroll-button-end {
- inset-inline-end: 0;
-}
-
-/*
- * Top
- */
-
-.tab-group-top {
- flex-direction: column;
-}
-
-.tab-group-top .nav-container {
- order: 1;
-}
-
-.tab-group-top .nav {
- display: flex;
- overflow-x: auto;
-
- /* Hide scrollbar in Firefox */
- scrollbar-width: none;
-}
-
-/* Hide scrollbar in Chrome/Safari */
-.tab-group-top .nav::-webkit-scrollbar {
- width: 0;
- height: 0;
-}
-
-.tab-group-top .tabs {
- flex: 1 1 auto;
- position: relative;
- flex-direction: row;
- border-bottom: solid var(--track-width) var(--track-color);
-}
-
-.tab-group-top .indicator {
- bottom: calc(-1 * var(--track-width));
- border-bottom: solid var(--track-width) var(--indicator-color);
-}
-
-.tab-group-top .body {
- order: 2;
-}
-
-.tab-group-top ::slotted(wa-tab[active]) {
- border-block-end: solid var(--track-width) var(--indicator-color);
- margin-block-end: calc(-1 * var(--track-width));
-}
-
-.tab-group-top ::slotted(wa-tab-panel) {
- --padding: var(--wa-space-xl) 0;
-}
-
-/*
- * Bottom
- */
-
-.tab-group-bottom {
- flex-direction: column;
-}
-
-.tab-group-bottom .nav-container {
- order: 2;
-}
-
-.tab-group-bottom .nav {
- display: flex;
- overflow-x: auto;
-
- /* Hide scrollbar in Firefox */
- scrollbar-width: none;
-}
-
-/* Hide scrollbar in Chrome/Safari */
-.tab-group-bottom .nav::-webkit-scrollbar {
- width: 0;
- height: 0;
-}
-
-.tab-group-bottom .tabs {
- flex: 1 1 auto;
- position: relative;
- flex-direction: row;
- border-top: solid var(--track-width) var(--track-color);
-}
-
-.tab-group-bottom .indicator {
- top: calc(-1 * var(--track-width));
- border-top: solid var(--track-width) var(--indicator-color);
-}
-
-.tab-group-bottom .body {
- order: 1;
-}
-
-.tab-group-bottom ::slotted(wa-tab[active]) {
- border-block-start: solid var(--track-width) var(--indicator-color);
- margin-block-start: calc(-1 * var(--track-width));
-}
-
-.tab-group-bottom ::slotted(wa-tab-panel) {
- --padding: var(--wa-space-xl) 0;
-}
-
-/*
- * Start
- */
-
-.tab-group-start {
- flex-direction: row;
-}
-
-.tab-group-start .nav-container {
- order: 1;
-}
-
-.tab-group-start .tabs {
- flex: 0 0 auto;
- flex-direction: column;
- border-inline-end: solid var(--track-width) var(--track-color);
-}
-
-.tab-group-start .indicator {
- inset-inline-end: calc(-1 * var(--track-width));
- border-right: solid var(--track-width) var(--indicator-color);
-}
-
-.tab-group-start .body {
- flex: 1 1 auto;
- order: 2;
-}
-
-.tab-group-start ::slotted(wa-tab[active]) {
- border-inline-end: solid var(--track-width) var(--indicator-color);
- margin-inline-end: calc(-1 * var(--track-width));
-}
-
-.tab-group-start ::slotted(wa-tab-panel) {
- --padding: 0 var(--wa-space-xl);
-}
-
-/*
- * End
- */
-
-.tab-group-end {
- flex-direction: row;
-}
-
-.tab-group-end .nav-container {
- order: 2;
-}
-
-.tab-group-end .tabs {
- flex: 0 0 auto;
- flex-direction: column;
- border-left: solid var(--track-width) var(--track-color);
-}
-
-.tab-group-end .indicator {
- inset-inline-start: calc(-1 * var(--track-width));
- border-inline-start: solid var(--track-width) var(--indicator-color);
-}
-
-.tab-group-end .body {
- flex: 1 1 auto;
- order: 1;
-}
-
-.tab-group-end ::slotted(wa-tab[active]) {
- border-inline-start: solid var(--track-width) var(--indicator-color);
- margin-inline-start: calc(-1 * var(--track-width));
-}
-
-.tab-group-end ::slotted(wa-tab-panel) {
- --padding: 0 var(--wa-space-xl);
-}
diff --git a/packages/webawesome/src/components/tab-group/tab-group.styles.ts b/packages/webawesome/src/components/tab-group/tab-group.styles.ts
new file mode 100644
index 000000000..ce98145fb
--- /dev/null
+++ b/packages/webawesome/src/components/tab-group/tab-group.styles.ts
@@ -0,0 +1,228 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --indicator-color: var(--wa-color-brand-fill-loud);
+ --track-color: var(--wa-color-neutral-fill-normal);
+ --track-width: 0.125rem;
+
+ display: block;
+ }
+
+ .tab-group {
+ display: flex;
+ border-radius: 0;
+ }
+
+ .tabs {
+ display: flex;
+ position: relative;
+ }
+
+ .indicator {
+ position: absolute;
+ }
+
+ .tab-group-has-scroll-controls .nav-container {
+ position: relative;
+ padding: 0 1.5em;
+ }
+
+ .body {
+ display: block;
+ }
+
+ .scroll-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 1.5em;
+ }
+
+ .scroll-button-start {
+ inset-inline-start: 0;
+ }
+
+ .scroll-button-end {
+ inset-inline-end: 0;
+ }
+
+ /*
+ * Top
+ */
+
+ .tab-group-top {
+ flex-direction: column;
+ }
+
+ .tab-group-top .nav-container {
+ order: 1;
+ }
+
+ .tab-group-top .nav {
+ display: flex;
+ overflow-x: auto;
+
+ /* Hide scrollbar in Firefox */
+ scrollbar-width: none;
+ }
+
+ /* Hide scrollbar in Chrome/Safari */
+ .tab-group-top .nav::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+ }
+
+ .tab-group-top .tabs {
+ flex: 1 1 auto;
+ position: relative;
+ flex-direction: row;
+ border-bottom: solid var(--track-width) var(--track-color);
+ }
+
+ .tab-group-top .indicator {
+ bottom: calc(-1 * var(--track-width));
+ border-bottom: solid var(--track-width) var(--indicator-color);
+ }
+
+ .tab-group-top .body {
+ order: 2;
+ }
+
+ .tab-group-top ::slotted(wa-tab[active]) {
+ border-block-end: solid var(--track-width) var(--indicator-color);
+ margin-block-end: calc(-1 * var(--track-width));
+ }
+
+ .tab-group-top ::slotted(wa-tab-panel) {
+ --padding: var(--wa-space-xl) 0;
+ }
+
+ /*
+ * Bottom
+ */
+
+ .tab-group-bottom {
+ flex-direction: column;
+ }
+
+ .tab-group-bottom .nav-container {
+ order: 2;
+ }
+
+ .tab-group-bottom .nav {
+ display: flex;
+ overflow-x: auto;
+
+ /* Hide scrollbar in Firefox */
+ scrollbar-width: none;
+ }
+
+ /* Hide scrollbar in Chrome/Safari */
+ .tab-group-bottom .nav::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+ }
+
+ .tab-group-bottom .tabs {
+ flex: 1 1 auto;
+ position: relative;
+ flex-direction: row;
+ border-top: solid var(--track-width) var(--track-color);
+ }
+
+ .tab-group-bottom .indicator {
+ top: calc(-1 * var(--track-width));
+ border-top: solid var(--track-width) var(--indicator-color);
+ }
+
+ .tab-group-bottom .body {
+ order: 1;
+ }
+
+ .tab-group-bottom ::slotted(wa-tab[active]) {
+ border-block-start: solid var(--track-width) var(--indicator-color);
+ margin-block-start: calc(-1 * var(--track-width));
+ }
+
+ .tab-group-bottom ::slotted(wa-tab-panel) {
+ --padding: var(--wa-space-xl) 0;
+ }
+
+ /*
+ * Start
+ */
+
+ .tab-group-start {
+ flex-direction: row;
+ }
+
+ .tab-group-start .nav-container {
+ order: 1;
+ }
+
+ .tab-group-start .tabs {
+ flex: 0 0 auto;
+ flex-direction: column;
+ border-inline-end: solid var(--track-width) var(--track-color);
+ }
+
+ .tab-group-start .indicator {
+ inset-inline-end: calc(-1 * var(--track-width));
+ border-right: solid var(--track-width) var(--indicator-color);
+ }
+
+ .tab-group-start .body {
+ flex: 1 1 auto;
+ order: 2;
+ }
+
+ .tab-group-start ::slotted(wa-tab[active]) {
+ border-inline-end: solid var(--track-width) var(--indicator-color);
+ margin-inline-end: calc(-1 * var(--track-width));
+ }
+
+ .tab-group-start ::slotted(wa-tab-panel) {
+ --padding: 0 var(--wa-space-xl);
+ }
+
+ /*
+ * End
+ */
+
+ .tab-group-end {
+ flex-direction: row;
+ }
+
+ .tab-group-end .nav-container {
+ order: 2;
+ }
+
+ .tab-group-end .tabs {
+ flex: 0 0 auto;
+ flex-direction: column;
+ border-left: solid var(--track-width) var(--track-color);
+ }
+
+ .tab-group-end .indicator {
+ inset-inline-start: calc(-1 * var(--track-width));
+ border-inline-start: solid var(--track-width) var(--indicator-color);
+ }
+
+ .tab-group-end .body {
+ flex: 1 1 auto;
+ order: 1;
+ }
+
+ .tab-group-end ::slotted(wa-tab[active]) {
+ border-inline-start: solid var(--track-width) var(--indicator-color);
+ margin-inline-start: calc(-1 * var(--track-width));
+ }
+
+ .tab-group-end ::slotted(wa-tab-panel) {
+ --padding: 0 var(--wa-space-xl);
+ }
+`;
diff --git a/packages/webawesome/src/components/tab-group/tab-group.ts b/packages/webawesome/src/components/tab-group/tab-group.ts
index abd684f9f..2421cd96c 100644
--- a/packages/webawesome/src/components/tab-group/tab-group.ts
+++ b/packages/webawesome/src/components/tab-group/tab-group.ts
@@ -12,7 +12,7 @@ import '../tab-panel/tab-panel.js';
import type WaTabPanel from '../tab-panel/tab-panel.js';
import '../tab/tab.js';
import type WaTab from '../tab/tab.js';
-import styles from './tab-group.css';
+import styles from './tab-group.styles.js';
/**
* @summary Tab groups organize content into a container that shows one section at a time.
diff --git a/packages/webawesome/src/components/tab-panel/tab-panel.css b/packages/webawesome/src/components/tab-panel/tab-panel.css
deleted file mode 100644
index cc74c9f1c..000000000
--- a/packages/webawesome/src/components/tab-panel/tab-panel.css
+++ /dev/null
@@ -1,14 +0,0 @@
-:host {
- --padding: 0;
-
- display: none;
-}
-
-:host([active]) {
- display: block;
-}
-
-.tab-panel {
- display: block;
- padding: var(--padding);
-}
diff --git a/packages/webawesome/src/components/tab-panel/tab-panel.styles.ts b/packages/webawesome/src/components/tab-panel/tab-panel.styles.ts
new file mode 100644
index 000000000..e0c9f2142
--- /dev/null
+++ b/packages/webawesome/src/components/tab-panel/tab-panel.styles.ts
@@ -0,0 +1,18 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --padding: 0;
+
+ display: none;
+ }
+
+ :host([active]) {
+ display: block;
+ }
+
+ .tab-panel {
+ display: block;
+ padding: var(--padding);
+ }
+`;
diff --git a/packages/webawesome/src/components/tab-panel/tab-panel.ts b/packages/webawesome/src/components/tab-panel/tab-panel.ts
index 1974f92f8..53d9b239d 100644
--- a/packages/webawesome/src/components/tab-panel/tab-panel.ts
+++ b/packages/webawesome/src/components/tab-panel/tab-panel.ts
@@ -3,7 +3,7 @@ import { customElement, property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import styles from './tab-panel.css';
+import styles from './tab-panel.styles.js';
let id = 0;
diff --git a/packages/webawesome/src/components/tab/tab.css b/packages/webawesome/src/components/tab/tab.css
deleted file mode 100644
index ef87dd807..000000000
--- a/packages/webawesome/src/components/tab/tab.css
+++ /dev/null
@@ -1,56 +0,0 @@
-:host {
- display: inline-block;
- color: var(--wa-color-neutral-on-quiet);
- font-weight: var(--wa-font-weight-action);
-}
-
-.tab {
- display: inline-flex;
- align-items: center;
- font: inherit;
- padding: 1em 1.5em;
- white-space: nowrap;
- user-select: none;
- -webkit-user-select: none;
- cursor: pointer;
- transition: color var(--wa-transition-fast) var(--wa-transition-easing);
-
- ::slotted(wa-icon:first-child) {
- margin-inline-end: 0.5em;
- }
-
- ::slotted(wa-icon:last-child) {
- margin-inline-start: 0.5em;
- }
-}
-
-@media (hover: hover) {
- :host(:hover:not([disabled])) .tab {
- color: currentColor;
- }
-}
-
-:host(:focus) {
- outline: transparent;
-}
-
-:host(:focus-visible) .tab {
- outline: var(--wa-focus-ring);
- outline-offset: calc(-1 * var(--wa-border-width-l) - var(--wa-focus-ring-offset));
-}
-
-:host([active]:not([disabled])) {
- color: var(--wa-color-brand-on-quiet);
-}
-
-:host([disabled]) .tab {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-@media (forced-colors: active) {
- :host([active]:not([disabled])) {
- outline: solid 1px transparent;
- outline-offset: -3px;
- }
-}
diff --git a/packages/webawesome/src/components/tab/tab.styles.ts b/packages/webawesome/src/components/tab/tab.styles.ts
new file mode 100644
index 000000000..61977cff2
--- /dev/null
+++ b/packages/webawesome/src/components/tab/tab.styles.ts
@@ -0,0 +1,60 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ display: inline-block;
+ color: var(--wa-color-neutral-on-quiet);
+ font-weight: var(--wa-font-weight-action);
+ }
+
+ .tab {
+ display: inline-flex;
+ align-items: center;
+ font: inherit;
+ padding: 1em 1.5em;
+ white-space: nowrap;
+ user-select: none;
+ -webkit-user-select: none;
+ cursor: pointer;
+ transition: color var(--wa-transition-fast) var(--wa-transition-easing);
+
+ ::slotted(wa-icon:first-child) {
+ margin-inline-end: 0.5em;
+ }
+
+ ::slotted(wa-icon:last-child) {
+ margin-inline-start: 0.5em;
+ }
+ }
+
+ @media (hover: hover) {
+ :host(:hover:not([disabled])) .tab {
+ color: currentColor;
+ }
+ }
+
+ :host(:focus) {
+ outline: transparent;
+ }
+
+ :host(:focus-visible) .tab {
+ outline: var(--wa-focus-ring);
+ outline-offset: calc(-1 * var(--wa-border-width-l) - var(--wa-focus-ring-offset));
+ }
+
+ :host([active]:not([disabled])) {
+ color: var(--wa-color-brand-on-quiet);
+ }
+
+ :host([disabled]) .tab {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ @media (forced-colors: active) {
+ :host([active]:not([disabled])) {
+ outline: solid 1px transparent;
+ outline-offset: -3px;
+ }
+ }
+`;
diff --git a/packages/webawesome/src/components/tab/tab.ts b/packages/webawesome/src/components/tab/tab.ts
index aedb2f988..546d41a45 100644
--- a/packages/webawesome/src/components/tab/tab.ts
+++ b/packages/webawesome/src/components/tab/tab.ts
@@ -3,7 +3,7 @@ import { customElement, property, query } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import styles from './tab.css';
+import styles from './tab.styles.js';
let id = 0;
diff --git a/packages/webawesome/src/components/tag/tag.css b/packages/webawesome/src/components/tag/tag.css
deleted file mode 100644
index 6289ad822..000000000
--- a/packages/webawesome/src/components/tag/tag.css
+++ /dev/null
@@ -1,78 +0,0 @@
-@layer wa-component {
- :host {
- display: inline-flex;
- gap: 0.5em;
- border-radius: var(--wa-border-radius-m);
- align-items: center;
- background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
- border-color: var(--wa-color-border-normal, var(--wa-color-neutral-border-normal));
- border-style: var(--wa-border-style);
- border-width: var(--wa-border-width-s);
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- font-size: inherit;
- line-height: 1;
- white-space: nowrap;
- user-select: none;
- -webkit-user-select: none;
- height: calc(var(--wa-form-control-height) * 0.8);
- line-height: calc(var(--wa-form-control-height) - var(--wa-form-control-border-width) * 2);
- padding: 0 0.75em;
- }
-
- /* Appearance modifiers */
- :host([appearance='outlined']) {
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- background-color: transparent;
- border-color: var(--wa-color-border-loud, var(--wa-color-neutral-border-loud));
- }
-
- :host([appearance='filled']) {
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
- border-color: transparent;
- }
-
- :host([appearance='filled-outlined']) {
- color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
- background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
- border-color: var(--wa-color-border-normal, var(--wa-color-neutral-border-normal));
- }
-
- :host([appearance='accent']) {
- color: var(--wa-color-on-loud, var(--wa-color-neutral-on-loud));
- background-color: var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud));
- border-color: transparent;
- }
-}
-
-.content {
- font-size: var(--wa-font-size-smaller);
-}
-
-[part='remove-button'] {
- color: inherit;
- line-height: 1;
-}
-
-[part='remove-button']::part(base) {
- padding: 0;
- height: 1em;
- width: 1em;
-}
-
-@media (hover: hover) {
- :host(:hover) > [part='remove-button']::part(base) {
- color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
- }
-}
-
-:host(:active) > [part='remove-button']::part(base) {
- color: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
-}
-
-/*
- * Pill modifier
- */
-:host([pill]) {
- border-radius: var(--wa-border-radius-pill);
-}
diff --git a/packages/webawesome/src/components/tag/tag.styles.ts b/packages/webawesome/src/components/tag/tag.styles.ts
new file mode 100644
index 000000000..17d91c99c
--- /dev/null
+++ b/packages/webawesome/src/components/tag/tag.styles.ts
@@ -0,0 +1,82 @@
+import { css } from 'lit';
+
+export default css`
+ @layer wa-component {
+ :host {
+ display: inline-flex;
+ gap: 0.5em;
+ border-radius: var(--wa-border-radius-m);
+ align-items: center;
+ background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+ border-color: var(--wa-color-border-normal, var(--wa-color-neutral-border-normal));
+ border-style: var(--wa-border-style);
+ border-width: var(--wa-border-width-s);
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ font-size: inherit;
+ line-height: 1;
+ white-space: nowrap;
+ user-select: none;
+ -webkit-user-select: none;
+ height: calc(var(--wa-form-control-height) * 0.8);
+ line-height: calc(var(--wa-form-control-height) - var(--wa-form-control-border-width) * 2);
+ padding: 0 0.75em;
+ }
+
+ /* Appearance modifiers */
+ :host([appearance='outlined']) {
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ background-color: transparent;
+ border-color: var(--wa-color-border-loud, var(--wa-color-neutral-border-loud));
+ }
+
+ :host([appearance='filled']) {
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+ border-color: transparent;
+ }
+
+ :host([appearance='filled-outlined']) {
+ color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+ background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+ border-color: var(--wa-color-border-normal, var(--wa-color-neutral-border-normal));
+ }
+
+ :host([appearance='accent']) {
+ color: var(--wa-color-on-loud, var(--wa-color-neutral-on-loud));
+ background-color: var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud));
+ border-color: transparent;
+ }
+ }
+
+ .content {
+ font-size: var(--wa-font-size-smaller);
+ }
+
+ [part='remove-button'] {
+ color: inherit;
+ line-height: 1;
+ }
+
+ [part='remove-button']::part(base) {
+ padding: 0;
+ height: 1em;
+ width: 1em;
+ }
+
+ @media (hover: hover) {
+ :host(:hover) > [part='remove-button']::part(base) {
+ color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
+ }
+ }
+
+ :host(:active) > [part='remove-button']::part(base) {
+ color: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
+ }
+
+ /*
+ * Pill modifier
+ */
+ :host([pill]) {
+ border-radius: var(--wa-border-radius-pill);
+ }
+`;
diff --git a/packages/webawesome/src/components/tag/tag.ts b/packages/webawesome/src/components/tag/tag.ts
index d2d1fbbe2..f72bf5b14 100644
--- a/packages/webawesome/src/components/tag/tag.ts
+++ b/packages/webawesome/src/components/tag/tag.ts
@@ -2,11 +2,11 @@ import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { WaRemoveEvent } from '../../events/remove.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
-import sizeStyles from '../../styles/utilities/size.css';
-import variantStyles from '../../styles/utilities/variants.css';
+import sizeStyles from '../../styles/component/size.styles.js';
+import variantStyles from '../../styles/component/variants.styles.js';
import { LocalizeController } from '../../utilities/localize.js';
import '../button/button.js';
-import styles from './tag.css';
+import styles from './tag.styles.js';
/**
* @summary Tags are used as labels to organize things or to indicate a selection.
diff --git a/packages/webawesome/src/components/textarea/textarea.css b/packages/webawesome/src/components/textarea/textarea.css
deleted file mode 100644
index da7091971..000000000
--- a/packages/webawesome/src/components/textarea/textarea.css
+++ /dev/null
@@ -1,120 +0,0 @@
-:host {
- border-width: 0;
-}
-
-.textarea {
- display: grid;
- align-items: center;
- margin: 0;
- border: none;
- outline: none;
- cursor: inherit;
- font: inherit;
- background-color: var(--wa-form-control-background-color);
- border-color: var(--wa-form-control-border-color);
- border-radius: var(--wa-form-control-border-radius);
- border-style: var(--wa-form-control-border-style);
- border-width: var(--wa-form-control-border-width);
- -webkit-appearance: none;
-
- &:focus-within {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
- }
-}
-
-/* Appearance modifiers */
-:host([appearance='outlined']) .textarea {
- background-color: var(--wa-form-control-background-color);
- border-color: var(--wa-form-control-border-color);
-}
-
-:host([appearance='filled']) .textarea {
- background-color: var(--wa-color-neutral-fill-quiet);
- border-color: var(--wa-color-neutral-fill-quiet);
-}
-
-:host([appearance='filled-outlined']) .textarea {
- background-color: var(--wa-color-neutral-fill-quiet);
- border-color: var(--wa-form-control-border-color);
-}
-
-textarea {
- display: block;
- width: 100%;
- border: none;
- background: transparent;
- font: inherit;
- color: inherit;
- padding: calc(var(--wa-form-control-padding-block) - ((1lh - 1em) / 2)) var(--wa-form-control-padding-inline); /* accounts for the larger line height of textarea content */
- min-height: calc(var(--wa-form-control-height) - var(--border-width) * 2);
- box-shadow: none;
- margin: 0;
-
- &::placeholder {
- color: var(--wa-form-control-placeholder-color);
- user-select: none;
- -webkit-user-select: none;
- }
-
- &:autofill {
- &,
- &:hover,
- &:focus,
- &:active {
- box-shadow: none;
- caret-color: var(--wa-form-control-value-color);
- }
- }
-
- &:focus {
- outline: none;
- }
-}
-
-/* Shared textarea and size-adjuster positioning */
-.control,
-.size-adjuster {
- grid-area: 1 / 1 / 2 / 2;
-}
-
-.size-adjuster {
- visibility: hidden;
- pointer-events: none;
- opacity: 0;
- padding: 0;
-}
-
-textarea::-webkit-search-decoration,
-textarea::-webkit-search-cancel-button,
-textarea::-webkit-search-results-button,
-textarea::-webkit-search-results-decoration {
- -webkit-appearance: none;
-}
-
-/*
- * Resize types
- */
-
-:host([resize='none']) textarea {
- resize: none;
-}
-
-textarea,
-:host([resize='vertical']) textarea {
- resize: vertical;
-}
-
-:host([resize='horizontal']) textarea {
- resize: horizontal;
-}
-
-:host([resize='both']) textarea {
- resize: both;
-}
-
-:host([resize='auto']) textarea {
- height: auto;
- resize: none;
- overflow-y: hidden;
-}
diff --git a/packages/webawesome/src/components/textarea/textarea.styles.ts b/packages/webawesome/src/components/textarea/textarea.styles.ts
new file mode 100644
index 000000000..4db3e6e16
--- /dev/null
+++ b/packages/webawesome/src/components/textarea/textarea.styles.ts
@@ -0,0 +1,124 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ border-width: 0;
+ }
+
+ .textarea {
+ display: grid;
+ align-items: center;
+ margin: 0;
+ border: none;
+ outline: none;
+ cursor: inherit;
+ font: inherit;
+ background-color: var(--wa-form-control-background-color);
+ border-color: var(--wa-form-control-border-color);
+ border-radius: var(--wa-form-control-border-radius);
+ border-style: var(--wa-form-control-border-style);
+ border-width: var(--wa-form-control-border-width);
+ -webkit-appearance: none;
+
+ &:focus-within {
+ outline: var(--wa-focus-ring);
+ outline-offset: var(--wa-focus-ring-offset);
+ }
+ }
+
+ /* Appearance modifiers */
+ :host([appearance='outlined']) .textarea {
+ background-color: var(--wa-form-control-background-color);
+ border-color: var(--wa-form-control-border-color);
+ }
+
+ :host([appearance='filled']) .textarea {
+ background-color: var(--wa-color-neutral-fill-quiet);
+ border-color: var(--wa-color-neutral-fill-quiet);
+ }
+
+ :host([appearance='filled-outlined']) .textarea {
+ background-color: var(--wa-color-neutral-fill-quiet);
+ border-color: var(--wa-form-control-border-color);
+ }
+
+ textarea {
+ display: block;
+ width: 100%;
+ border: none;
+ background: transparent;
+ font: inherit;
+ color: inherit;
+ padding: calc(var(--wa-form-control-padding-block) - ((1lh - 1em) / 2)) var(--wa-form-control-padding-inline); /* accounts for the larger line height of textarea content */
+ min-height: calc(var(--wa-form-control-height) - var(--border-width) * 2);
+ box-shadow: none;
+ margin: 0;
+
+ &::placeholder {
+ color: var(--wa-form-control-placeholder-color);
+ user-select: none;
+ -webkit-user-select: none;
+ }
+
+ &:autofill {
+ &,
+ &:hover,
+ &:focus,
+ &:active {
+ box-shadow: none;
+ caret-color: var(--wa-form-control-value-color);
+ }
+ }
+
+ &:focus {
+ outline: none;
+ }
+ }
+
+ /* Shared textarea and size-adjuster positioning */
+ .control,
+ .size-adjuster {
+ grid-area: 1 / 1 / 2 / 2;
+ }
+
+ .size-adjuster {
+ visibility: hidden;
+ pointer-events: none;
+ opacity: 0;
+ padding: 0;
+ }
+
+ textarea::-webkit-search-decoration,
+ textarea::-webkit-search-cancel-button,
+ textarea::-webkit-search-results-button,
+ textarea::-webkit-search-results-decoration {
+ -webkit-appearance: none;
+ }
+
+ /*
+ * Resize types
+ */
+
+ :host([resize='none']) textarea {
+ resize: none;
+ }
+
+ textarea,
+ :host([resize='vertical']) textarea {
+ resize: vertical;
+ }
+
+ :host([resize='horizontal']) textarea {
+ resize: horizontal;
+ }
+
+ :host([resize='both']) textarea {
+ resize: both;
+ }
+
+ :host([resize='auto']) textarea {
+ height: auto;
+ resize: none;
+ overflow-y: hidden;
+ }
+`;
diff --git a/packages/webawesome/src/components/textarea/textarea.ts b/packages/webawesome/src/components/textarea/textarea.ts
index 954b325be..3e7870f7d 100644
--- a/packages/webawesome/src/components/textarea/textarea.ts
+++ b/packages/webawesome/src/components/textarea/textarea.ts
@@ -8,9 +8,9 @@ import { HasSlotController } from '../../internal/slot.js';
import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
-import formControlStyles from '../../styles/component/form-control.css';
-import sizeStyles from '../../styles/utilities/size.css';
-import styles from './textarea.css';
+import formControlStyles from '../../styles/component/form-control.styles.js';
+import sizeStyles from '../../styles/component/size.styles.js';
+import styles from './textarea.styles.js';
/**
* @summary Textareas collect data from the user and allow multiple lines of text.
@@ -107,13 +107,6 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
/** Makes the textarea readonly. */
@property({ type: Boolean, reflect: true }) readonly = false;
- /**
- * By default, form controls are associated with the nearest containing ` ` element. This attribute allows you
- * to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
- * the same document or shadow root for this to work.
- */
- @property({ reflect: true }) form = null;
-
/** Makes the textarea a required field. */
@property({ type: Boolean, reflect: true }) required = false;
diff --git a/packages/webawesome/src/components/tooltip/tooltip.css b/packages/webawesome/src/components/tooltip/tooltip.css
deleted file mode 100644
index 6dc3d2ebc..000000000
--- a/packages/webawesome/src/components/tooltip/tooltip.css
+++ /dev/null
@@ -1,56 +0,0 @@
-:host {
- --max-width: 30ch;
-
- /** These styles are added so we don't interfere in the DOM. */
- display: inline-block;
- position: absolute;
-
- /** Defaults for inherited CSS properties */
- color: var(--wa-tooltip-content-color);
- font-size: var(--wa-tooltip-font-size);
- line-height: var(--wa-tooltip-line-height);
- text-align: start;
- white-space: normal;
-}
-
-.tooltip {
- --arrow-size: var(--wa-tooltip-arrow-size);
- --arrow-color: var(--wa-tooltip-background-color);
-}
-
-.tooltip::part(popup) {
- z-index: 1000;
-}
-
-.tooltip[placement^='top']::part(popup) {
- transform-origin: bottom;
-}
-
-.tooltip[placement^='bottom']::part(popup) {
- transform-origin: top;
-}
-
-.tooltip[placement^='left']::part(popup) {
- transform-origin: right;
-}
-
-.tooltip[placement^='right']::part(popup) {
- transform-origin: left;
-}
-
-.body {
- display: block;
- width: max-content;
- max-width: var(--max-width);
- border-radius: var(--wa-tooltip-border-radius);
- background-color: var(--wa-tooltip-background-color);
- border: var(--wa-tooltip-border-width) var(--wa-tooltip-border-style) var(--wa-tooltip-border-color);
- padding: 0.25em 0.5em;
- user-select: none;
- -webkit-user-select: none;
-}
-
-.tooltip::part(arrow) {
- border-bottom: var(--wa-tooltip-border-width) var(--wa-tooltip-border-style) var(--wa-tooltip-border-color);
- border-right: var(--wa-tooltip-border-width) var(--wa-tooltip-border-style) var(--wa-tooltip-border-color);
-}
diff --git a/packages/webawesome/src/components/tooltip/tooltip.styles.ts b/packages/webawesome/src/components/tooltip/tooltip.styles.ts
new file mode 100644
index 000000000..6bb5be759
--- /dev/null
+++ b/packages/webawesome/src/components/tooltip/tooltip.styles.ts
@@ -0,0 +1,60 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --max-width: 30ch;
+
+ /** These styles are added so we don't interfere in the DOM. */
+ display: inline-block;
+ position: absolute;
+
+ /** Defaults for inherited CSS properties */
+ color: var(--wa-tooltip-content-color);
+ font-size: var(--wa-tooltip-font-size);
+ line-height: var(--wa-tooltip-line-height);
+ text-align: start;
+ white-space: normal;
+ }
+
+ .tooltip {
+ --arrow-size: var(--wa-tooltip-arrow-size);
+ --arrow-color: var(--wa-tooltip-background-color);
+ }
+
+ .tooltip::part(popup) {
+ z-index: 1000;
+ }
+
+ .tooltip[placement^='top']::part(popup) {
+ transform-origin: bottom;
+ }
+
+ .tooltip[placement^='bottom']::part(popup) {
+ transform-origin: top;
+ }
+
+ .tooltip[placement^='left']::part(popup) {
+ transform-origin: right;
+ }
+
+ .tooltip[placement^='right']::part(popup) {
+ transform-origin: left;
+ }
+
+ .body {
+ display: block;
+ width: max-content;
+ max-width: var(--max-width);
+ border-radius: var(--wa-tooltip-border-radius);
+ background-color: var(--wa-tooltip-background-color);
+ border: var(--wa-tooltip-border-width) var(--wa-tooltip-border-style) var(--wa-tooltip-border-color);
+ padding: 0.25em 0.5em;
+ user-select: none;
+ -webkit-user-select: none;
+ }
+
+ .tooltip::part(arrow) {
+ border-bottom: var(--wa-tooltip-border-width) var(--wa-tooltip-border-style) var(--wa-tooltip-border-color);
+ border-right: var(--wa-tooltip-border-width) var(--wa-tooltip-border-style) var(--wa-tooltip-border-color);
+ }
+`;
diff --git a/packages/webawesome/src/components/tooltip/tooltip.ts b/packages/webawesome/src/components/tooltip/tooltip.ts
index abbbcfd85..db5d6bebd 100644
--- a/packages/webawesome/src/components/tooltip/tooltip.ts
+++ b/packages/webawesome/src/components/tooltip/tooltip.ts
@@ -11,7 +11,7 @@ import { uniqueId } from '../../internal/math.js';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import WaPopup from '../popup/popup.js';
-import styles from './tooltip.css';
+import styles from './tooltip.styles.js';
/**
* @summary Tooltips display additional information based on a specific action.
diff --git a/packages/webawesome/src/components/tree-item/tree-item.css b/packages/webawesome/src/components/tree-item/tree-item.css
deleted file mode 100644
index a34ed4d3d..000000000
--- a/packages/webawesome/src/components/tree-item/tree-item.css
+++ /dev/null
@@ -1,150 +0,0 @@
-:host {
- --show-duration: 200ms;
- --hide-duration: 200ms;
-
- display: block;
- color: var(--wa-color-text-normal);
- outline: 0;
- z-index: 0;
-}
-
-:host(:focus) {
- outline: none;
-}
-
-slot:not([name])::slotted(wa-icon) {
- margin-inline-end: var(--wa-space-xs);
-}
-
-.tree-item {
- position: relative;
- display: flex;
- align-items: stretch;
- flex-direction: column;
- cursor: default;
- user-select: none;
- -webkit-user-select: none;
-}
-
-.checkbox {
- line-height: var(--wa-form-control-value-line-height);
- pointer-events: none;
-}
-
-.expand-button,
-.checkbox,
-.label {
- font-family: inherit;
- font-size: var(--wa-font-size-m);
- font-weight: inherit;
-}
-
-.checkbox::part(base) {
- display: flex;
- align-items: center;
-}
-
-.indentation {
- display: block;
- width: 1em;
- flex-shrink: 0;
-}
-
-.expand-button {
- display: flex;
- align-items: center;
- justify-content: center;
- color: var(--wa-color-text-quiet);
- width: 2em;
- height: 2em;
- flex-shrink: 0;
- cursor: pointer;
-}
-
-.expand-button {
- transition: rotate var(--wa-transition-normal) var(--wa-transition-easing);
-}
-
-.tree-item-expanded .expand-button {
- rotate: 90deg;
-}
-
-.tree-item-expanded:dir(rtl) .expand-button {
- rotate: -90deg;
-}
-
-.tree-item-expanded:not(.tree-item-loading) slot[name='expand-icon'],
-.tree-item:not(.tree-item-expanded) slot[name='collapse-icon'] {
- display: none;
-}
-
-.tree-item:not(.tree-item-has-expand-button):not(.tree-item-loading) .expand-icon-slot {
- display: none;
-}
-
-.tree-item-loading .expand-icon-slot wa-icon {
- display: none;
-}
-
-.expand-button-visible {
- cursor: pointer;
-}
-
-.item {
- display: flex;
- align-items: center;
- border-inline-start: solid 3px transparent;
-}
-
-:host([disabled]) .item {
- opacity: 0.5;
- outline: none;
- cursor: not-allowed;
-}
-
-:host(:focus-visible) .item {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
- z-index: 2;
-}
-
-:host(:not([aria-disabled='true'])) .tree-item-selected .item {
- background-color: var(--wa-color-neutral-fill-quiet);
- border-inline-start-color: var(--wa-color-brand-fill-loud);
-}
-
-:host(:not([aria-disabled='true'])) .expand-button {
- color: var(--wa-color-text-quiet);
-}
-
-.label {
- display: flex;
- align-items: center;
- transition: color var(--wa-transition-normal) var(--wa-transition-easing);
-}
-
-.children {
- display: block;
- font-size: calc(1em + var(--indent-size, var(--wa-space-m)));
-}
-
-/* Indentation lines */
-.children {
- position: relative;
-}
-
-.children::before {
- content: '';
- position: absolute;
- top: var(--indent-guide-offset);
- bottom: var(--indent-guide-offset);
- inset-inline-start: calc(1em - (var(--indent-guide-width) / 2) - 1px);
- border-inline-end: var(--indent-guide-width) var(--indent-guide-style) var(--indent-guide-color);
- z-index: 1;
-}
-
-@media (forced-colors: active) {
- :host(:not([aria-disabled='true'])) .tree-item-selected .item {
- outline: dashed 1px SelectedItem;
- }
-}
diff --git a/packages/webawesome/src/components/tree-item/tree-item.styles.ts b/packages/webawesome/src/components/tree-item/tree-item.styles.ts
new file mode 100644
index 000000000..e2c49c6fc
--- /dev/null
+++ b/packages/webawesome/src/components/tree-item/tree-item.styles.ts
@@ -0,0 +1,154 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ --show-duration: 200ms;
+ --hide-duration: 200ms;
+
+ display: block;
+ color: var(--wa-color-text-normal);
+ outline: 0;
+ z-index: 0;
+ }
+
+ :host(:focus) {
+ outline: none;
+ }
+
+ slot:not([name])::slotted(wa-icon) {
+ margin-inline-end: var(--wa-space-xs);
+ }
+
+ .tree-item {
+ position: relative;
+ display: flex;
+ align-items: stretch;
+ flex-direction: column;
+ cursor: default;
+ user-select: none;
+ -webkit-user-select: none;
+ }
+
+ .checkbox {
+ line-height: var(--wa-form-control-value-line-height);
+ pointer-events: none;
+ }
+
+ .expand-button,
+ .checkbox,
+ .label {
+ font-family: inherit;
+ font-size: var(--wa-font-size-m);
+ font-weight: inherit;
+ }
+
+ .checkbox::part(base) {
+ display: flex;
+ align-items: center;
+ }
+
+ .indentation {
+ display: block;
+ width: 1em;
+ flex-shrink: 0;
+ }
+
+ .expand-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--wa-color-text-quiet);
+ width: 2em;
+ height: 2em;
+ flex-shrink: 0;
+ cursor: pointer;
+ }
+
+ .expand-button {
+ transition: rotate var(--wa-transition-normal) var(--wa-transition-easing);
+ }
+
+ .tree-item-expanded .expand-button {
+ rotate: 90deg;
+ }
+
+ .tree-item-expanded:dir(rtl) .expand-button {
+ rotate: -90deg;
+ }
+
+ .tree-item-expanded:not(.tree-item-loading) slot[name='expand-icon'],
+ .tree-item:not(.tree-item-expanded) slot[name='collapse-icon'] {
+ display: none;
+ }
+
+ .tree-item:not(.tree-item-has-expand-button):not(.tree-item-loading) .expand-icon-slot {
+ display: none;
+ }
+
+ .tree-item-loading .expand-icon-slot wa-icon {
+ display: none;
+ }
+
+ .expand-button-visible {
+ cursor: pointer;
+ }
+
+ .item {
+ display: flex;
+ align-items: center;
+ border-inline-start: solid 3px transparent;
+ }
+
+ :host([disabled]) .item {
+ opacity: 0.5;
+ outline: none;
+ cursor: not-allowed;
+ }
+
+ :host(:focus-visible) .item {
+ outline: var(--wa-focus-ring);
+ outline-offset: var(--wa-focus-ring-offset);
+ z-index: 2;
+ }
+
+ :host(:not([aria-disabled='true'])) .tree-item-selected .item {
+ background-color: var(--wa-color-neutral-fill-quiet);
+ border-inline-start-color: var(--wa-color-brand-fill-loud);
+ }
+
+ :host(:not([aria-disabled='true'])) .expand-button {
+ color: var(--wa-color-text-quiet);
+ }
+
+ .label {
+ display: flex;
+ align-items: center;
+ transition: color var(--wa-transition-normal) var(--wa-transition-easing);
+ }
+
+ .children {
+ display: block;
+ font-size: calc(1em + var(--indent-size, var(--wa-space-m)));
+ }
+
+ /* Indentation lines */
+ .children {
+ position: relative;
+ }
+
+ .children::before {
+ content: '';
+ position: absolute;
+ top: var(--indent-guide-offset);
+ bottom: var(--indent-guide-offset);
+ inset-inline-start: calc(1em - (var(--indent-guide-width) / 2) - 1px);
+ border-inline-end: var(--indent-guide-width) var(--indent-guide-style) var(--indent-guide-color);
+ z-index: 1;
+ }
+
+ @media (forced-colors: active) {
+ :host(:not([aria-disabled='true'])) .tree-item-selected .item {
+ outline: dashed 1px SelectedItem;
+ }
+ }
+`;
diff --git a/packages/webawesome/src/components/tree-item/tree-item.ts b/packages/webawesome/src/components/tree-item/tree-item.ts
index dc15888d6..2a1ff4cf0 100644
--- a/packages/webawesome/src/components/tree-item/tree-item.ts
+++ b/packages/webawesome/src/components/tree-item/tree-item.ts
@@ -17,7 +17,7 @@ import { LocalizeController } from '../../utilities/localize.js';
import '../checkbox/checkbox.js';
import '../icon/icon.js';
import '../spinner/spinner.js';
-import styles from './tree-item.css';
+import styles from './tree-item.styles.js';
/**
* @summary A tree item serves as a hierarchical node that lives inside a [tree](/docs/components/tree).
diff --git a/packages/webawesome/src/components/tree/tree.css b/packages/webawesome/src/components/tree/tree.styles.ts
similarity index 53%
rename from packages/webawesome/src/components/tree/tree.css
rename to packages/webawesome/src/components/tree/tree.styles.ts
index b5ca3878e..666100ae2 100644
--- a/packages/webawesome/src/components/tree/tree.css
+++ b/packages/webawesome/src/components/tree/tree.styles.ts
@@ -1,19 +1,23 @@
-:host {
- /*
+import { css } from 'lit';
+
+export default css`
+ :host {
+ /*
* These are actually used by tree item, but we define them here so they can more easily be set and all tree items
* stay consistent.
*/
- --indent-guide-color: var(--wa-color-surface-border);
- --indent-guide-offset: 0;
- --indent-guide-style: solid;
- --indent-guide-width: 0;
- --indent-size: var(--wa-space-l);
+ --indent-guide-color: var(--wa-color-surface-border);
+ --indent-guide-offset: 0;
+ --indent-guide-style: solid;
+ --indent-guide-width: 0;
+ --indent-size: var(--wa-space-l);
- display: block;
+ display: block;
- /*
+ /*
* Tree item indentation uses the "em" unit to increment its width on each level, so setting the font size to zero
* here removes the indentation for all the nodes on the first level.
*/
- font-size: 0;
-}
+ font-size: 0;
+ }
+`;
diff --git a/packages/webawesome/src/components/tree/tree.ts b/packages/webawesome/src/components/tree/tree.ts
index e82dab664..762cb7d44 100644
--- a/packages/webawesome/src/components/tree/tree.ts
+++ b/packages/webawesome/src/components/tree/tree.ts
@@ -6,7 +6,7 @@ import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
import WaTreeItem from '../tree-item/tree-item.js';
-import styles from './tree.css';
+import styles from './tree.styles.js';
function syncCheckboxes(changedTreeItem: WaTreeItem, initialSync = false) {
function syncParentItem(treeItem: WaTreeItem) {
diff --git a/packages/webawesome/src/components/zoomable-frame/zoomable-frame.css b/packages/webawesome/src/components/zoomable-frame/zoomable-frame.css
deleted file mode 100644
index fbd1af03b..000000000
--- a/packages/webawesome/src/components/zoomable-frame/zoomable-frame.css
+++ /dev/null
@@ -1,82 +0,0 @@
-:host {
- display: block;
- position: relative;
- aspect-ratio: 16 / 9;
- width: 100%;
- overflow: hidden;
- border-radius: var(--wa-border-radius-m);
-}
-
-#frame-container {
- position: absolute;
- top: 0;
- left: 0;
- width: calc(100% / var(--zoom));
- height: calc(100% / var(--zoom));
- transform: scale(var(--zoom));
- transform-origin: 0 0;
-}
-
-#iframe {
- width: 100%;
- height: 100%;
- border: none;
- border-radius: inherit;
- /* Prevent the iframe from being selected, e.g. by a double click. Doesn't affect selection withing the iframe. */
- user-select: none;
- -webkit-user-select: none;
-}
-
-#controls {
- display: flex;
- position: absolute;
- bottom: 0.5em;
- align-items: center;
- font-weight: var(--wa-font-weight-semibold);
- padding: 0.25em 0.5em;
- gap: 0.5em;
- border-radius: var(--wa-border-radius-s);
- background: #000b;
- color: white;
- font-size: min(12px, 0.75em);
- user-select: none;
- -webkit-user-select: none;
-
- &:dir(ltr) {
- right: 0.5em;
- }
-
- &:dir(rtl) {
- left: 0.5em;
- }
-
- button {
- display: flex;
- align-items: center;
- padding: 0.25em;
- border: none;
- background: none;
- color: inherit;
- cursor: pointer;
-
- &:focus {
- outline: none;
- }
-
- &:focus-visible {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
- }
-
- &:disabled {
- cursor: not-allowed;
- opacity: 0.5;
- }
- }
-
- span {
- min-width: 4.5ch; /* extra space so numbers don't shift */
- font-variant-numeric: tabular-nums;
- text-align: center;
- }
-}
diff --git a/packages/webawesome/src/components/zoomable-frame/zoomable-frame.styles.ts b/packages/webawesome/src/components/zoomable-frame/zoomable-frame.styles.ts
new file mode 100644
index 000000000..a40151dec
--- /dev/null
+++ b/packages/webawesome/src/components/zoomable-frame/zoomable-frame.styles.ts
@@ -0,0 +1,86 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ display: block;
+ position: relative;
+ aspect-ratio: 16 / 9;
+ width: 100%;
+ overflow: hidden;
+ border-radius: var(--wa-border-radius-m);
+ }
+
+ #frame-container {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: calc(100% / var(--zoom));
+ height: calc(100% / var(--zoom));
+ transform: scale(var(--zoom));
+ transform-origin: 0 0;
+ }
+
+ #iframe {
+ width: 100%;
+ height: 100%;
+ border: none;
+ border-radius: inherit;
+ /* Prevent the iframe from being selected, e.g. by a double click. Doesn't affect selection withing the iframe. */
+ user-select: none;
+ -webkit-user-select: none;
+ }
+
+ #controls {
+ display: flex;
+ position: absolute;
+ bottom: 0.5em;
+ align-items: center;
+ font-weight: var(--wa-font-weight-semibold);
+ padding: 0.25em 0.5em;
+ gap: 0.5em;
+ border-radius: var(--wa-border-radius-s);
+ background: #000b;
+ color: white;
+ font-size: min(12px, 0.75em);
+ user-select: none;
+ -webkit-user-select: none;
+
+ &:dir(ltr) {
+ right: 0.5em;
+ }
+
+ &:dir(rtl) {
+ left: 0.5em;
+ }
+
+ button {
+ display: flex;
+ align-items: center;
+ padding: 0.25em;
+ border: none;
+ background: none;
+ color: inherit;
+ cursor: pointer;
+
+ &:focus {
+ outline: none;
+ }
+
+ &:focus-visible {
+ outline: var(--wa-focus-ring);
+ outline-offset: var(--wa-focus-ring-offset);
+ }
+
+ &:disabled {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+ }
+
+ span {
+ min-width: 4.5ch; /* extra space so numbers don't shift */
+ font-variant-numeric: tabular-nums;
+ text-align: center;
+ }
+ }
+`;
diff --git a/packages/webawesome/src/components/zoomable-frame/zoomable-frame.ts b/packages/webawesome/src/components/zoomable-frame/zoomable-frame.ts
index 6414d6313..1b2854e87 100644
--- a/packages/webawesome/src/components/zoomable-frame/zoomable-frame.ts
+++ b/packages/webawesome/src/components/zoomable-frame/zoomable-frame.ts
@@ -5,7 +5,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
import { parseSpaceDelimitedTokens } from '../../internal/parse.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
-import styles from './zoomable-frame.css';
+import styles from './zoomable-frame.styles.js';
/**
* @summary Zoomable frames render iframe content with zoom and interaction controls.
diff --git a/packages/webawesome/src/internal/webawesome-element.ts b/packages/webawesome/src/internal/webawesome-element.ts
index 68faf36e1..ef0e54fd5 100644
--- a/packages/webawesome/src/internal/webawesome-element.ts
+++ b/packages/webawesome/src/internal/webawesome-element.ts
@@ -1,13 +1,13 @@
-import type { CSSResult, CSSResultGroup, PropertyValues } from 'lit';
-import { LitElement, isServer, unsafeCSS } from 'lit';
+import type { CSSResultGroup, PropertyValues } from 'lit';
+import { LitElement, isServer } from 'lit';
import { property } from 'lit/decorators.js';
-import hostStyles from '../styles/component/host.css';
+import hostStyles from '../styles/component/host.styles.js';
// Augment Lit's module
declare module 'lit' {
interface PropertyDeclaration {
/**
- * Specifies the property’s default value
+ * Specifies the property's default value
*/
default?: any;
initial?: any;
@@ -15,20 +15,13 @@ declare module 'lit' {
}
export default class WebAwesomeElement extends LitElement {
- /**
- * One or more CSS files to include in the component's shadow root. Host styles are automatically prepended. We use
- * this instead of Lit's styles property because we're importing CSS files as strings and need to convert them using
- * unsafeCSS.
- */
- static css?: CSSResultGroup | CSSResult | string | (CSSResult | string)[];
+ /** One or more CSSResultGroup to include in the component's shadow root. Host styles are automatically prepended. */
+ static css?: CSSResultGroup;
- /**
- * Override the default styles property to fetch and convert string CSS files. Components can override this behavior
- * by setting their own `static styles = []` property.
- */
+ /** Prepends host styles to the component's styles. */
static get styles(): CSSResultGroup {
const styles = Array.isArray(this.css) ? this.css : this.css ? [this.css] : [];
- return [hostStyles, ...styles].map(style => (typeof style === 'string' ? unsafeCSS(style) : style));
+ return [hostStyles, ...styles];
}
#hasRecordedInitialProperties = false;
diff --git a/packages/webawesome/src/internal/webawesome-form-associated-element.ts b/packages/webawesome/src/internal/webawesome-form-associated-element.ts
index f84116325..440f13aee 100644
--- a/packages/webawesome/src/internal/webawesome-form-associated-element.ts
+++ b/packages/webawesome/src/internal/webawesome-form-associated-element.ts
@@ -23,7 +23,8 @@ export interface WebAwesomeFormControl extends WebAwesomeElement {
checked?: boolean;
defaultSelected?: boolean;
selected?: boolean;
- form?: string | null;
+ get form(): HTMLFormElement | null;
+ set form(val: string);
value?: unknown;
@@ -203,6 +204,23 @@ export class WebAwesomeFormAssociatedElement
return this.internals.form;
}
+ /**
+ * By default, form controls are associated with the nearest containing ` ` element. This attribute allows you
+ * to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
+ * the same document or shadow root for this to work.
+ */
+ set form(val: string) {
+ if (val) {
+ this.setAttribute('form', val);
+ } else {
+ this.removeAttribute('form');
+ }
+ }
+
+ get form(): HTMLFormElement | null {
+ return this.internals.form;
+ }
+
@property({ attribute: false, state: true, type: Object })
get validity() {
return this.internals.validity;
diff --git a/packages/webawesome/src/styles/component/form-control.css b/packages/webawesome/src/styles/component/form-control.css
deleted file mode 100644
index f9e1efb77..000000000
--- a/packages/webawesome/src/styles/component/form-control.css
+++ /dev/null
@@ -1,34 +0,0 @@
-:host {
- display: flex;
- flex-direction: column;
-}
-
-/* Label */
-:is([part~='form-control-label'], [part~='label']):has(*:not(:empty)) {
- display: inline-flex;
- color: var(--wa-form-control-label-color);
- font-weight: var(--wa-form-control-label-font-weight);
- line-height: var(--wa-form-control-label-line-height);
- margin-block-end: 0.5em;
-}
-
-:host([required]) :is([part~='form-control-label'], [part~='label'])::after {
- content: var(--wa-form-control-required-content);
- margin-inline-start: var(--wa-form-control-required-content-offset);
- color: var(--wa-form-control-required-content-color);
-}
-
-/* Help text */
-[part~='hint'] {
- display: block;
- color: var(--wa-form-control-hint-color);
- font-weight: var(--wa-form-control-hint-font-weight);
- line-height: var(--wa-form-control-hint-line-height);
- margin-block-start: 0.5em;
- font-size: var(--wa-font-size-smaller);
- line-height: var(--wa-form-control-label-line-height);
-
- &:not(.has-slotted) {
- display: none;
- }
-}
diff --git a/packages/webawesome/src/styles/component/form-control.styles.ts b/packages/webawesome/src/styles/component/form-control.styles.ts
new file mode 100644
index 000000000..461d732e5
--- /dev/null
+++ b/packages/webawesome/src/styles/component/form-control.styles.ts
@@ -0,0 +1,38 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ display: flex;
+ flex-direction: column;
+ }
+
+ /* Label */
+ :is([part~='form-control-label'], [part~='label']):has(*:not(:empty)) {
+ display: inline-flex;
+ color: var(--wa-form-control-label-color);
+ font-weight: var(--wa-form-control-label-font-weight);
+ line-height: var(--wa-form-control-label-line-height);
+ margin-block-end: 0.5em;
+ }
+
+ :host([required]) :is([part~='form-control-label'], [part~='label'])::after {
+ content: var(--wa-form-control-required-content);
+ margin-inline-start: var(--wa-form-control-required-content-offset);
+ color: var(--wa-form-control-required-content-color);
+ }
+
+ /* Help text */
+ [part~='hint'] {
+ display: block;
+ color: var(--wa-form-control-hint-color);
+ font-weight: var(--wa-form-control-hint-font-weight);
+ line-height: var(--wa-form-control-hint-line-height);
+ margin-block-start: 0.5em;
+ font-size: var(--wa-font-size-smaller);
+ line-height: var(--wa-form-control-label-line-height);
+
+ &:not(.has-slotted) {
+ display: none;
+ }
+ }
+`;
diff --git a/packages/webawesome/src/styles/component/host.css b/packages/webawesome/src/styles/component/host.css
deleted file mode 100644
index 7239526e1..000000000
--- a/packages/webawesome/src/styles/component/host.css
+++ /dev/null
@@ -1,13 +0,0 @@
-:host {
- box-sizing: border-box !important;
-}
-
-:host *,
-:host *::before,
-:host *::after {
- box-sizing: inherit !important;
-}
-
-[hidden] {
- display: none !important;
-}
diff --git a/packages/webawesome/src/styles/component/host.styles.ts b/packages/webawesome/src/styles/component/host.styles.ts
new file mode 100644
index 000000000..510b37dbd
--- /dev/null
+++ b/packages/webawesome/src/styles/component/host.styles.ts
@@ -0,0 +1,17 @@
+import { css } from 'lit';
+
+export default css`
+ :host {
+ box-sizing: border-box !important;
+ }
+
+ :host *,
+ :host *::before,
+ :host *::after {
+ box-sizing: inherit !important;
+ }
+
+ [hidden] {
+ display: none !important;
+ }
+`;
diff --git a/packages/webawesome/src/styles/component/size.styles.ts b/packages/webawesome/src/styles/component/size.styles.ts
new file mode 100644
index 000000000..2b06e5862
--- /dev/null
+++ b/packages/webawesome/src/styles/component/size.styles.ts
@@ -0,0 +1,18 @@
+import { css } from 'lit';
+
+export default css`
+ :host([size='small']),
+ .wa-size-s {
+ font-size: var(--wa-font-size-s);
+ }
+
+ :host([size='medium']),
+ .wa-size-m {
+ font-size: var(--wa-font-size-m);
+ }
+
+ :host([size='large']),
+ .wa-size-l {
+ font-size: var(--wa-font-size-l);
+ }
+`;
diff --git a/packages/webawesome/src/styles/component/variants.styles.ts b/packages/webawesome/src/styles/component/variants.styles.ts
new file mode 100644
index 000000000..2498610ad
--- /dev/null
+++ b/packages/webawesome/src/styles/component/variants.styles.ts
@@ -0,0 +1,69 @@
+import { css } from 'lit';
+
+export default css`
+ :where(:root),
+ .wa-neutral,
+ :host([variant='neutral']) {
+ --wa-color-fill-loud: var(--wa-color-neutral-fill-loud);
+ --wa-color-fill-normal: var(--wa-color-neutral-fill-normal);
+ --wa-color-fill-quiet: var(--wa-color-neutral-fill-quiet);
+ --wa-color-border-loud: var(--wa-color-neutral-border-loud);
+ --wa-color-border-normal: var(--wa-color-neutral-border-normal);
+ --wa-color-border-quiet: var(--wa-color-neutral-border-quiet);
+ --wa-color-on-loud: var(--wa-color-neutral-on-loud);
+ --wa-color-on-normal: var(--wa-color-neutral-on-normal);
+ --wa-color-on-quiet: var(--wa-color-neutral-on-quiet);
+ }
+
+ .wa-brand,
+ :host([variant='brand']) {
+ --wa-color-fill-loud: var(--wa-color-brand-fill-loud);
+ --wa-color-fill-normal: var(--wa-color-brand-fill-normal);
+ --wa-color-fill-quiet: var(--wa-color-brand-fill-quiet);
+ --wa-color-border-loud: var(--wa-color-brand-border-loud);
+ --wa-color-border-normal: var(--wa-color-brand-border-normal);
+ --wa-color-border-quiet: var(--wa-color-brand-border-quiet);
+ --wa-color-on-loud: var(--wa-color-brand-on-loud);
+ --wa-color-on-normal: var(--wa-color-brand-on-normal);
+ --wa-color-on-quiet: var(--wa-color-brand-on-quiet);
+ }
+
+ .wa-success,
+ :host([variant='success']) {
+ --wa-color-fill-loud: var(--wa-color-success-fill-loud);
+ --wa-color-fill-normal: var(--wa-color-success-fill-normal);
+ --wa-color-fill-quiet: var(--wa-color-success-fill-quiet);
+ --wa-color-border-loud: var(--wa-color-success-border-loud);
+ --wa-color-border-normal: var(--wa-color-success-border-normal);
+ --wa-color-border-quiet: var(--wa-color-success-border-quiet);
+ --wa-color-on-loud: var(--wa-color-success-on-loud);
+ --wa-color-on-normal: var(--wa-color-success-on-normal);
+ --wa-color-on-quiet: var(--wa-color-success-on-quiet);
+ }
+
+ .wa-warning,
+ :host([variant='warning']) {
+ --wa-color-fill-loud: var(--wa-color-warning-fill-loud);
+ --wa-color-fill-normal: var(--wa-color-warning-fill-normal);
+ --wa-color-fill-quiet: var(--wa-color-warning-fill-quiet);
+ --wa-color-border-loud: var(--wa-color-warning-border-loud);
+ --wa-color-border-normal: var(--wa-color-warning-border-normal);
+ --wa-color-border-quiet: var(--wa-color-warning-border-quiet);
+ --wa-color-on-loud: var(--wa-color-warning-on-loud);
+ --wa-color-on-normal: var(--wa-color-warning-on-normal);
+ --wa-color-on-quiet: var(--wa-color-warning-on-quiet);
+ }
+
+ .wa-danger,
+ :host([variant='danger']) {
+ --wa-color-fill-loud: var(--wa-color-danger-fill-loud);
+ --wa-color-fill-normal: var(--wa-color-danger-fill-normal);
+ --wa-color-fill-quiet: var(--wa-color-danger-fill-quiet);
+ --wa-color-border-loud: var(--wa-color-danger-border-loud);
+ --wa-color-border-normal: var(--wa-color-danger-border-normal);
+ --wa-color-border-quiet: var(--wa-color-danger-border-quiet);
+ --wa-color-on-loud: var(--wa-color-danger-on-loud);
+ --wa-color-on-normal: var(--wa-color-danger-on-normal);
+ --wa-color-on-quiet: var(--wa-color-danger-on-quiet);
+ }
+`;
diff --git a/packages/webawesome/src/styles/component/visually-hidden.styles.ts b/packages/webawesome/src/styles/component/visually-hidden.styles.ts
new file mode 100644
index 000000000..866bae7a7
--- /dev/null
+++ b/packages/webawesome/src/styles/component/visually-hidden.styles.ts
@@ -0,0 +1,18 @@
+import { css } from 'lit';
+
+export default css`
+ .wa-visually-hidden:not(:focus-within),
+ .wa-visually-hidden-force,
+ .wa-visually-hidden-hint::part(hint),
+ .wa-visually-hidden-label::part(label) {
+ position: absolute !important;
+ width: 1px !important;
+ height: 1px !important;
+ clip: rect(0 0 0 0) !important;
+ clip-path: inset(50%) !important;
+ border: none !important;
+ overflow: hidden !important;
+ white-space: nowrap !important;
+ padding: 0 !important;
+ }
+`;
diff --git a/packages/webawesome/src/styles/utilities/size.css b/packages/webawesome/src/styles/utilities/size.css
index ca8a43f36..6ad984091 100644
--- a/packages/webawesome/src/styles/utilities/size.css
+++ b/packages/webawesome/src/styles/utilities/size.css
@@ -1,15 +1,12 @@
@layer wa-utilities {
- :host([size='small']),
.wa-size-s {
font-size: var(--wa-font-size-s);
}
- :host([size='medium']),
.wa-size-m {
font-size: var(--wa-font-size-m);
}
- :host([size='large']),
.wa-size-l {
font-size: var(--wa-font-size-l);
}
diff --git a/packages/webawesome/src/styles/utilities/variants.css b/packages/webawesome/src/styles/utilities/variants.css
index df7eb08ec..b93da35a3 100644
--- a/packages/webawesome/src/styles/utilities/variants.css
+++ b/packages/webawesome/src/styles/utilities/variants.css
@@ -1,7 +1,6 @@
@layer wa-utilities {
:where(:root),
- .wa-neutral,
- :host([variant='neutral']) {
+ .wa-neutral {
--wa-color-fill-loud: var(--wa-color-neutral-fill-loud);
--wa-color-fill-normal: var(--wa-color-neutral-fill-normal);
--wa-color-fill-quiet: var(--wa-color-neutral-fill-quiet);
@@ -13,8 +12,7 @@
--wa-color-on-quiet: var(--wa-color-neutral-on-quiet);
}
- .wa-brand,
- :host([variant='brand']) {
+ .wa-brand {
--wa-color-fill-loud: var(--wa-color-brand-fill-loud);
--wa-color-fill-normal: var(--wa-color-brand-fill-normal);
--wa-color-fill-quiet: var(--wa-color-brand-fill-quiet);
@@ -26,8 +24,7 @@
--wa-color-on-quiet: var(--wa-color-brand-on-quiet);
}
- .wa-success,
- :host([variant='success']) {
+ .wa-success {
--wa-color-fill-loud: var(--wa-color-success-fill-loud);
--wa-color-fill-normal: var(--wa-color-success-fill-normal);
--wa-color-fill-quiet: var(--wa-color-success-fill-quiet);
@@ -39,8 +36,7 @@
--wa-color-on-quiet: var(--wa-color-success-on-quiet);
}
- .wa-warning,
- :host([variant='warning']) {
+ .wa-warning {
--wa-color-fill-loud: var(--wa-color-warning-fill-loud);
--wa-color-fill-normal: var(--wa-color-warning-fill-normal);
--wa-color-fill-quiet: var(--wa-color-warning-fill-quiet);
@@ -52,8 +48,7 @@
--wa-color-on-quiet: var(--wa-color-warning-on-quiet);
}
- .wa-danger,
- :host([variant='danger']) {
+ .wa-danger {
--wa-color-fill-loud: var(--wa-color-danger-fill-loud);
--wa-color-fill-normal: var(--wa-color-danger-fill-normal);
--wa-color-fill-quiet: var(--wa-color-danger-fill-quiet);