diff --git a/src/lib/components/ui/checkbox/Checkbox.svelte b/src/lib/components/ui/checkbox/Checkbox.svelte
index 31e1706a..60d9d632 100644
--- a/src/lib/components/ui/checkbox/Checkbox.svelte
+++ b/src/lib/components/ui/checkbox/Checkbox.svelte
@@ -1,19 +1,27 @@
+
{#snippet children({ checked: isChecked, indeterminate: isIndeterminate })}
{#if isIndeterminate}
- −
+
{:else if isChecked}
- ✓
+ {@html CheckIcon}
{/if}
{/snippet}
@@ -66,96 +79,140 @@
@use '$src/themes/typography' as *;
@use '$src/themes/effects' as *;
- .checkbox {
+ // Base checkbox styles
+ :global(.checkbox) {
+ // Default (no element) colors - light grey bg with dark grey check
+ --cb-checked-bg: var(--null-bg);
+ --cb-checked-bg-hover: var(--null-bg-hover);
+ --cb-checked-fg: #{$grey-45};
+
background-color: var(--input-bg);
- border: 2px solid var(--separator-bg);
- border-radius: $item-corner-small;
+ border: none;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
@include smooth-transition($duration-zoom, all);
+ }
- &:hover:not(:disabled) {
- background-color: var(--input-bg-hover);
- border-color: var(--separator-bg-hover);
- }
+ :global(.checkbox:hover:not(:disabled)) {
+ background-color: var(--input-bg-hover);
+ }
- &:focus,
- &:focus-visible {
- @include focus-ring($blue);
- }
+ :global(.checkbox:focus),
+ :global(.checkbox:focus-visible) {
+ @include focus-ring($blue);
+ }
- &[data-state='checked'],
- &[data-state='indeterminate'] {
- background-color: var(--accent-blue);
- border-color: var(--accent-blue);
+ :global(.checkbox[data-state='checked']),
+ :global(.checkbox[data-state='indeterminate']) {
+ background-color: var(--cb-checked-bg);
+ }
- &:hover:not(:disabled) {
- background-color: var(--accent-blue-hover);
- border-color: var(--accent-blue-hover);
- }
- }
+ :global(.checkbox[data-state='checked']:hover:not(:disabled)),
+ :global(.checkbox[data-state='indeterminate']:hover:not(:disabled)) {
+ background-color: var(--cb-checked-bg-hover);
+ }
- &:disabled {
- cursor: not-allowed;
- opacity: 0.5;
- }
+ :global(.checkbox:disabled) {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
- &.bound {
- background-color: var(--input-bound-bg);
+ :global(.checkbox.bound) {
+ background-color: var(--input-bound-bg);
+ }
- &:hover:not(:disabled) {
- background-color: var(--input-bound-bg-hover);
- }
+ :global(.checkbox.bound:hover:not(:disabled)) {
+ background-color: var(--input-bound-bg-hover);
+ }
- &[data-state='checked'],
- &[data-state='indeterminate'] {
- background-color: var(--accent-blue);
- border-color: var(--accent-blue);
- }
- }
+ // Element-specific color overrides
+ :global(.checkbox.wind) {
+ --cb-checked-bg: var(--wind-button-bg);
+ --cb-checked-bg-hover: var(--wind-button-bg-hover);
+ --cb-checked-fg: white;
+ }
+
+ :global(.checkbox.fire) {
+ --cb-checked-bg: var(--fire-button-bg);
+ --cb-checked-bg-hover: var(--fire-button-bg-hover);
+ --cb-checked-fg: white;
+ }
+
+ :global(.checkbox.water) {
+ --cb-checked-bg: var(--water-button-bg);
+ --cb-checked-bg-hover: var(--water-button-bg-hover);
+ --cb-checked-fg: white;
+ }
+
+ :global(.checkbox.earth) {
+ --cb-checked-bg: var(--earth-button-bg);
+ --cb-checked-bg-hover: var(--earth-button-bg-hover);
+ --cb-checked-fg: white;
+ }
+
+ :global(.checkbox.dark) {
+ --cb-checked-bg: var(--dark-button-bg);
+ --cb-checked-bg-hover: var(--dark-button-bg-hover);
+ --cb-checked-fg: white;
+ }
+
+ :global(.checkbox.light) {
+ --cb-checked-bg: var(--light-button-bg);
+ --cb-checked-bg-hover: var(--light-button-bg-hover);
+ --cb-checked-fg: white;
}
// Size variations
- .small {
- width: $unit-2x;
- height: $unit-2x;
-
- .icon {
- width: calc($unit * 1.5);
- height: calc($unit * 1.5);
- }
- }
-
- .medium {
- width: calc($unit * 2.5);
- height: calc($unit * 2.5);
-
- .icon {
- width: calc($unit * 1.75);
- height: calc($unit * 1.75);
- }
- }
-
- .large {
+ :global(.checkbox.small) {
+ --cb-icon-size: #{calc($unit * 1.5)};
+ --cb-dash-height: 3px;
+ border-radius: $item-corner;
width: $unit-3x;
height: $unit-3x;
-
- .icon {
- width: calc($unit * 2.25);
- height: calc($unit * 2.25);
- }
}
- .indicator {
+ :global(.checkbox.medium) {
+ --cb-icon-size: #{$unit-2x};
+ --cb-dash-height: 4px;
+ border-radius: $card-corner;
+ width: $unit-4x;
+ height: $unit-4x;
+ }
+
+ :global(.checkbox.large) {
+ --cb-icon-size: #{calc($unit * 2.5)};
+ --cb-dash-height: 4px;
+ border-radius: $card-corner;
+ width: $unit-5x;
+ height: $unit-5x;
+ }
+
+ // Indicator and icon styles
+ :global(.checkbox .indicator) {
display: flex;
align-items: center;
justify-content: center;
- color: white;
+ color: var(--cb-checked-fg);
}
- .icon {
- stroke-width: 3;
+ :global(.checkbox .icon) {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ :global(svg) {
+ width: var(--cb-icon-size);
+ height: var(--cb-icon-size);
+ fill: currentColor;
+ }
+
+ &.indeterminate {
+ width: var(--cb-icon-size);
+ height: var(--cb-dash-height);
+ background-color: var(--cb-checked-fg);
+ border-radius: $unit-fourth;
+ }
}
diff --git a/src/lib/components/ui/switch/Switch.svelte b/src/lib/components/ui/switch/Switch.svelte
index 03b22e2b..0eaf1fe7 100644
--- a/src/lib/components/ui/switch/Switch.svelte
+++ b/src/lib/components/ui/switch/Switch.svelte
@@ -9,6 +9,12 @@
required?: boolean;
name?: string;
value?: string;
+ /** Switch size */
+ size?: 'small' | 'medium' | 'large';
+ /** Full width switch */
+ fullWidth?: boolean;
+ /** Element color theme for checked state */
+ element?: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light' | undefined;
onCheckedChange?: (checked: boolean) => void;
class?: string;
thumbClass?: string;
@@ -20,6 +26,9 @@
required = false,
name,
value,
+ size = 'medium',
+ fullWidth = false,
+ element,
onCheckedChange,
class: className,
thumbClass
@@ -30,6 +39,10 @@
onCheckedChange(checked);
}
});
+
+ const switchClass = $derived(
+ ['switch', size, fullWidth && 'full', element, className].filter(Boolean).join(' ')
+ );
@@ -49,53 +62,153 @@
@use '$src/themes/layout' as *;
@use '$src/themes/effects' as *;
- .switch {
- $height: calc($unit-4x + $unit-fourth); // 34px
+ // Base switch styles - wrapped in :global() for Bits UI
+ :global([data-switch-root].switch) {
+ // Default (no element) colors
+ --sw-checked-bg: var(--null-button-bg);
+ --sw-checked-bg-hover: var(--null-button-bg-hover);
+
background: $grey-70;
- border-radius: calc($height / 2);
- border: none;
- padding-left: $unit-half;
- padding-right: $unit-half;
+ border: 2px solid transparent;
+ box-sizing: border-box;
position: relative;
- width: $unit-7x + $unit-fourth; // 58px
- height: $height;
cursor: pointer;
@include smooth-transition($duration-instant, background-color);
-
- &:focus,
- &:focus-visible {
- @include focus-ring($blue);
- }
-
- &[data-state='checked'] {
- background: var(--accent-blue);
- }
-
- &:disabled {
- box-shadow: none;
- cursor: not-allowed;
- opacity: 0.5;
-
- .thumb {
- background: $grey-80;
- cursor: not-allowed;
- }
- }
}
- .thumb {
- background: $grey-100;
- border-radius: calc($unit-3x + $unit-fourth / 2); // 13px
- display: block;
+ :global([data-switch-root].switch:focus),
+ :global([data-switch-root].switch:focus-visible) {
+ @include focus-ring($blue);
+ }
+
+ :global([data-switch-root].switch:hover:not(:disabled)) {
+ background: $grey-75;
+ }
+
+ :global([data-switch-root].switch[data-state='checked']) {
+ background: var(--sw-checked-bg);
+ }
+
+ :global([data-switch-root].switch[data-state='checked']:hover:not(:disabled)) {
+ background: var(--sw-checked-bg-hover);
+ }
+
+ :global([data-switch-root].switch:disabled) {
+ box-shadow: none;
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+
+ :global([data-switch-root].switch.full) {
+ width: 100%;
+ }
+
+ // Element-specific color overrides
+ :global([data-switch-root].switch.wind) {
+ --sw-checked-bg: var(--wind-button-bg);
+ --sw-checked-bg-hover: var(--wind-button-bg-hover);
+ }
+
+ :global([data-switch-root].switch.fire) {
+ --sw-checked-bg: var(--fire-button-bg);
+ --sw-checked-bg-hover: var(--fire-button-bg-hover);
+ }
+
+ :global([data-switch-root].switch.water) {
+ --sw-checked-bg: var(--water-button-bg);
+ --sw-checked-bg-hover: var(--water-button-bg-hover);
+ }
+
+ :global([data-switch-root].switch.earth) {
+ --sw-checked-bg: var(--earth-button-bg);
+ --sw-checked-bg-hover: var(--earth-button-bg-hover);
+ }
+
+ :global([data-switch-root].switch.dark) {
+ --sw-checked-bg: var(--dark-button-bg);
+ --sw-checked-bg-hover: var(--dark-button-bg-hover);
+ }
+
+ :global([data-switch-root].switch.light) {
+ --sw-checked-bg: var(--light-button-bg);
+ --sw-checked-bg-hover: var(--light-button-bg-hover);
+ }
+
+ :global([data-switch-root].switch:disabled .thumb) {
+ background: $grey-80;
+ cursor: not-allowed;
+ }
+
+ // Size: Small
+ :global([data-switch-root].switch.small) {
+ $height: $unit-3x; // 24px
+ border-radius: calc($height / 2);
+ padding-left: $unit-fourth;
+ padding-right: $unit-fourth;
+ width: calc($unit-5x + $unit-half); // 44px
+ height: $height;
+ }
+
+ :global([data-switch-root].switch.small .thumb) {
+ height: calc($unit-2x + $unit-half); // 20px
+ width: calc($unit-2x + $unit-half); // 20px
+ border-radius: calc(($unit-2x + $unit-half) / 2);
+ }
+
+ :global([data-switch-root].switch.small .thumb[data-state='checked']) {
+ transform: translateX(calc($unit-2x + $unit-half)); // 20px
+ }
+
+ // Size: Medium (default)
+ :global([data-switch-root].switch.medium) {
+ $height: calc($unit-4x + $unit-fourth); // 34px
+ border-radius: calc($height / 2);
+ padding-left: $unit-half;
+ padding-right: $unit-half;
+ width: $unit-7x + $unit-fourth; // 58px
+ height: $height;
+ }
+
+ :global([data-switch-root].switch.medium .thumb) {
height: $unit-3x + $unit-fourth; // 26px
width: $unit-3x + $unit-fourth; // 26px
- @include smooth-transition($duration-instant, transform);
- transform: translateX(0px);
- cursor: pointer;
+ border-radius: calc(($unit-3x + $unit-fourth) / 2);
+ }
- &[data-state='checked'] {
- background: $grey-100;
- transform: translateX($unit-3x); // 24px
- }
+ :global([data-switch-root].switch.medium .thumb[data-state='checked']) {
+ transform: translateX(21px);
+ }
+
+ // Size: Large
+ :global([data-switch-root].switch.large) {
+ $height: $unit-5x; // 40px
+ border-radius: calc($height / 2);
+ padding-left: $unit-half;
+ padding-right: $unit-half;
+ width: calc($unit-8x + $unit); // 72px
+ height: $height;
+ }
+
+ :global([data-switch-root].switch.large .thumb) {
+ height: calc($unit-4x); // 32px
+ width: calc($unit-4x); // 32px
+ border-radius: $unit-2x;
+ }
+
+ :global([data-switch-root].switch.large .thumb[data-state='checked']) {
+ transform: translateX(calc($unit-4x)); // 32px
+ }
+
+ // Thumb base styles
+ :global([data-switch-root] .thumb) {
+ background: $grey-100;
+ display: block;
+ @include smooth-transition($duration-instant, transform);
+ transform: translateX(-1px);
+ cursor: pointer;
+ }
+
+ :global([data-switch-root] .thumb[data-state='checked']) {
+ background: $grey-100;
}
diff --git a/src/themes/_colors.scss b/src/themes/_colors.scss
index e10c9e20..076e155f 100644
--- a/src/themes/_colors.scss
+++ b/src/themes/_colors.scss
@@ -261,6 +261,16 @@ $input--bound--bg--light--hover: $grey-85;
$input--bound--bg--dark: $grey-15;
$input--bound--bg--dark--hover: $grey-10;
+// Color Definitions: Checkbox
+$checkbox--bg--light: $grey-100;
+$checkbox--bg--dark: $grey-10;
+$checkbox--bg--light--hover: $grey-95;
+$checkbox--bg--dark--hover: $grey-30;
+$checkbox--text--light: $grey-50;
+$checkbox--text--dark: $grey-80;
+$checkbox--text--light--hover: $grey-30;
+$checkbox--text--dark--hover: $grey-90;
+
// Color Definitions: Select
$select--trigger--bg--light: $grey-100;
$select--trigger--bg--dark: $grey-10;
diff --git a/src/themes/themes.scss b/src/themes/themes.scss
index b78bcc58..f3e792c8 100644
--- a/src/themes/themes.scss
+++ b/src/themes/themes.scss
@@ -1,3 +1,4 @@
+@use 'sass:color';
@use 'colors';
@use 'typography';
@use 'spacing';
@@ -150,6 +151,12 @@
--input-bound-bg: #{colors.$input--bound--bg--light};
--input-bound-bg-hover: #{colors.$input--bound--bg--light--hover};
+ // Light - Checkboxes
+ --checkbox-bg: #{colors.$checkbox--bg--light};
+ --checkbox-bg-hover: #{colors.$checkbox--bg--light--hover};
+ --checkbox-text: #{colors.$checkbox--text--light};
+ --checkbox-text-hover: #{colors.$checkbox--text--light--hover};
+
// Light - Selects
--select-bg: #{colors.$select--trigger--bg--light};
--select-contained-bg: #{colors.$select--trigger--contained--bg--light};
@@ -268,6 +275,15 @@
--light-button-bg: #{colors.$light-text-30};
--dark-button-bg: #{colors.$dark-text-30};
+ // Light - Element button hover colors (slightly lighter than button-bg)
+ --null-button-bg-hover: #{colors.$grey-60};
+ --wind-button-bg-hover: #{color.adjust(colors.$wind-text-30, $lightness: 8%)};
+ --fire-button-bg-hover: #{color.adjust(colors.$fire-text-30, $lightness: 8%)};
+ --water-button-bg-hover: #{color.adjust(colors.$water-text-30, $lightness: 8%)};
+ --earth-button-bg-hover: #{color.adjust(colors.$earth-text-20, $lightness: 8%)};
+ --light-button-bg-hover: #{color.adjust(colors.$light-text-30, $lightness: 8%)};
+ --dark-button-bg-hover: #{color.adjust(colors.$dark-text-30, $lightness: 8%)};
+
// Light - Element navigation selected background
--null-nav-selected-bg: #{colors.$grey-85};
--wind-nav-selected-bg: #{colors.$wind-bg-20};
@@ -486,6 +502,12 @@
--input-bound-bg: #{colors.$input--bound--bg--dark};
--input-bound-bg-hover: #{colors.$input--bound--bg--dark--hover};
+ // Dark - Checkboxes
+ --checkbox-bg: #{colors.$checkbox--bg--dark};
+ --checkbox-bg-hover: #{colors.$checkbox--bg--dark--hover};
+ --checkbox-text: #{colors.$checkbox--text--dark};
+ --checkbox-text-hover: #{colors.$checkbox--text--dark--hover};
+
// Dark - Selects
--select-bg: #{colors.$select--trigger--bg--dark};
--select-contained-bg: #{colors.$select--trigger--contained--bg--dark};
@@ -604,6 +626,15 @@
--light-button-bg: #{colors.$light-text-30};
--dark-button-bg: #{colors.$dark-text-30};
+ // Dark - Element button hover colors (same as light theme)
+ --null-button-bg-hover: #{colors.$grey-60};
+ --wind-button-bg-hover: #{color.adjust(colors.$wind-text-30, $lightness: 8%)};
+ --fire-button-bg-hover: #{color.adjust(colors.$fire-text-30, $lightness: 8%)};
+ --water-button-bg-hover: #{color.adjust(colors.$water-text-30, $lightness: 8%)};
+ --earth-button-bg-hover: #{color.adjust(colors.$earth-text-20, $lightness: 8%)};
+ --light-button-bg-hover: #{color.adjust(colors.$light-text-30, $lightness: 8%)};
+ --dark-button-bg-hover: #{color.adjust(colors.$dark-text-30, $lightness: 8%)};
+
// Dark - Element navigation selected background (same as light theme)
--null-nav-selected-bg: #{colors.$grey-85};
--wind-nav-selected-bg: #{colors.$wind-bg-20};