From bb60211e8ba016d55a627a145c016c462c714e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kuna?= <1282324+codecalm@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:09:43 +0200 Subject: [PATCH] Give the off switch a filled track and a white knob (#3271) --- .changeset/form-switch-off-track.md | 6 ++++++ core/scss/_variables.scss | 5 +++-- core/scss/bootstrap/forms/_form-check.scss | 22 ++++++++++++++++------ core/scss/ui/forms/_form-check.scss | 4 ++-- docs/content/ui/forms/css-variables.mdx | 5 +++-- 5 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 .changeset/form-switch-off-track.md diff --git a/.changeset/form-switch-off-track.md b/.changeset/form-switch-off-track.md new file mode 100644 index 000000000..443ce318d --- /dev/null +++ b/.changeset/form-switch-off-track.md @@ -0,0 +1,6 @@ +--- +"@tabler/core": patch +"@tabler/docs": patch +--- + +Updated `.form-switch` with a filled gray off track, a white knob with an even gap and a new `$form-switch-bg` variable. diff --git a/core/scss/_variables.scss b/core/scss/_variables.scss index c3e6084a6..e11f40dc2 100644 --- a/core/scss/_variables.scss +++ b/core/scss/_variables.scss @@ -2048,7 +2048,8 @@ $form-select-font-size-lg: $input-font-size-lg !default; $form-select-border-radius-lg: $input-border-radius-lg !default; $form-select-transition: $input-transition !default; -$form-switch-color: light-dark(var(--gray-200), #{color-transparent(var(--white), 0.25)}) !default; +$form-switch-bg: light-dark(var(--gray-300), #{color-transparent(var(--white), 0.25)}) !default; +$form-switch-color: var(--white) !default; $form-switch-width: 2rem !default; $form-switch-height: 1.25rem !default; $form-switch-padding-start: $form-switch-width + 0.5rem !default; @@ -2058,7 +2059,7 @@ $form-switch-transition: border-color 0.15s ease-in-out, $focus-ring-transition !default; $form-switch-knob-transition: inset-inline-start 0.15s ease-in-out !default; -$form-switch-focus-color: mix-color-css(var(--white), var(--primary), 50%) !default; +$form-switch-focus-color: var(--white) !default; $form-switch-checked-color: var(--white) !default; $form-switch-disabled-bg: $input-disabled-bg !default; $form-switch-disabled-color: light-dark(var(--gray-400), var(--gray-600)) !default; diff --git a/core/scss/bootstrap/forms/_form-check.scss b/core/scss/bootstrap/forms/_form-check.scss index 320bdd0d5..41cef1f09 100644 --- a/core/scss/bootstrap/forms/_form-check.scss +++ b/core/scss/bootstrap/forms/_form-check.scss @@ -157,6 +157,7 @@ --form-switch-height: #{$form-switch-height}; --form-switch-padding-start: #{if(sass($form-switch-padding-start == $form-switch-width + 0.5rem): calc(var(--form-switch-width) + 0.5rem); else: $form-switch-padding-start)}; --form-switch-border-radius: #{if(sass($form-switch-border-radius == $form-switch-width): var(--form-switch-width); else: $form-switch-border-radius)}; + --form-switch-bg: #{$form-switch-bg}; --form-switch-focus-color: #{$form-switch-focus-color}; --form-switch-checked-color: #{$form-switch-checked-color}; --form-switch-disabled-bg: #{var-if-default($form-switch-disabled-bg, var(--bg-forms-disabled), '--control-disabled-bg')}; @@ -167,10 +168,12 @@ .form-check-input { --form-switch-color: #{$form-switch-color}; - // The knob is a circle three quarters of the track height, inset by an eighth - // — the same geometry the old `viewBox='-4 -4 8 8'` svg had. - --form-switch-knob-size: calc(var(--form-switch-height) * 0.75); - --form-switch-knob-offset: calc(var(--form-switch-height) * 0.125); + // The knob is a circle with the same gap to the outer edge of the track on + // every side. The gap is a whole number of pixels, so the knob stays round. + --form-switch-knob-offset: 0.125rem; + --form-switch-knob-size: calc(var(--form-switch-height) - var(--form-switch-knob-offset) * 2); + // The knob is positioned inside the border, the gap is measured from outside it. + --form-switch-knob-inset: calc(var(--form-switch-knob-offset) - var(--border-width)); width: var(--form-switch-width); margin-inline-start: calc(var(--form-switch-padding-start) * -1); @@ -180,7 +183,7 @@ // The knob replaces the masked check icon: a plain circle that slides. &::before { inset-block: 0; - inset-inline: var(--form-switch-knob-offset) auto; + inset-inline: var(--form-switch-knob-inset) auto; width: var(--form-switch-knob-size); height: var(--form-switch-knob-size); margin-block: auto; @@ -192,6 +195,13 @@ @include transition($form-switch-knob-transition); } + // An off switch is a filled track with no outline, so it reads as a + // switch and not as an empty input. + &:not(:checked, :disabled) { + background-color: var(--form-switch-bg); + border-color: transparent; + } + &:focus { --form-switch-color: var(--form-switch-focus-color); } @@ -200,7 +210,7 @@ --form-switch-color: var(--form-switch-checked-color); &::before { - inset-inline-start: calc(100% - var(--form-switch-knob-size) - var(--form-switch-knob-offset)); + inset-inline-start: calc(100% - var(--form-switch-knob-size) - var(--form-switch-knob-inset)); } } } diff --git a/core/scss/ui/forms/_form-check.scss b/core/scss/ui/forms/_form-check.scss index 0d06f7686..f78be59ba 100644 --- a/core/scss/ui/forms/_form-check.scss +++ b/core/scss/ui/forms/_form-check.scss @@ -91,8 +91,8 @@ Form switch padding-inline-start: var(--form-switch-lg-padding-start); .form-check-input { - --form-switch-knob-size: calc(var(--form-switch-lg-height) * 0.75); - --form-switch-knob-offset: calc(var(--form-switch-lg-height) * 0.125); + --form-switch-knob-offset: 0.1875rem; + --form-switch-knob-size: calc(var(--form-switch-lg-height) - var(--form-switch-knob-offset) * 2); width: var(--form-switch-lg-width); height: var(--form-switch-lg-height); margin-inline-start: var(--form-switch-lg-input-offset); diff --git a/docs/content/ui/forms/css-variables.mdx b/docs/content/ui/forms/css-variables.mdx index 9e439ea72..91232438b 100644 --- a/docs/content/ui/forms/css-variables.mdx +++ b/docs/content/ui/forms/css-variables.mdx @@ -232,7 +232,7 @@ Set on `.form-range`. ### Switches -Set on `.form-switch`. The knob is three quarters of the height and slides by the width, so both follow these two tokens. +Set on `.form-switch`. The knob fills the height minus a `0.125rem` gap on every side and slides by the width, so both follow these two tokens. | Variable | Default | | --- | --- | @@ -240,7 +240,8 @@ Set on `.form-switch`. The knob is three quarters of the height and slides by th | `--tblr-form-switch-height` | `1.25rem` | | `--tblr-form-switch-padding-start` | width + `0.5rem` | | `--tblr-form-switch-border-radius` | `var(--tblr-form-switch-width)` | -| `--tblr-form-switch-focus-color` | primary mixed with white | +| `--tblr-form-switch-bg` | gray `300` in light mode, translucent white in dark (the off track) | +| `--tblr-form-switch-focus-color` | `var(--tblr-white)` (the knob) | | `--tblr-form-switch-checked-color` | `var(--tblr-white)` (the knob) | | `--tblr-form-switch-disabled-bg` | `var(--tblr-control-disabled-bg)` | | `--tblr-form-switch-disabled-color` | gray `400` in light mode, `600` in dark |