From 107e493275c48d94df632522ce01bb3abe5b3adc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Kuna?=
<1282324+codecalm@users.noreply.github.com>
Date: Sun, 13 Sep 2026 00:56:36 +0200
Subject: [PATCH] Define the color palette in `oklch()` (#3043)
---
.agents/skills/core-js/SKILL.md | 2 +-
.changeset/palette-oklch.md | 6 ++
core/js/src/tabler.ts | 17 ---
core/js/tabler.ts | 3 -
core/package.json | 8 +-
core/scss/_settings.scss | 4 +-
core/scss/_variables-dark.scss | 4 +-
core/scss/_variables.scss | 95 ++++++++---------
core/scss/bootstrap/_carousel.scss | 9 +-
core/scss/bootstrap/_navbar.scss | 13 ++-
core/scss/bootstrap/_reboot.scss | 2 +-
core/scss/bootstrap/forms/_form-check.scss | 82 +++++++++------
core/scss/layout/_navbar.scss | 1 +
core/scss/marketing/_hero.scss | 11 --
core/scss/mixins/_functions.scss | 109 ++-----------------
core/scss/mixins/_mixins.scss | 17 +++
core/scss/tabler-themes.scss | 110 ++++++++++----------
core/scss/tests/_breakpoints.test.scss | 10 +-
core/scss/tests/_colors-misc.test.scss | 10 --
core/scss/tests/_css-var.test.scss | 27 +----
core/scss/tests/_escape-svg.test.scss | 5 +
core/scss/tests/_maps.test.scss | 41 --------
core/scss/tests/_media-breakpoint.test.scss | 67 ------------
core/scss/ui/forms/_form-colorinput.scss | 4 +-
core/scss/ui/forms/_form-imagecheck.scss | 4 +-
core/scss/ui/forms/_form-selectgroup.scss | 4 +-
docs/content/ui/plugins/color-picker.mdx | 12 +--
shared/ui/Colorpicker.astro | 10 +-
28 files changed, 224 insertions(+), 463 deletions(-)
create mode 100644 .changeset/palette-oklch.md
delete mode 100644 core/js/src/tabler.ts
diff --git a/.agents/skills/core-js/SKILL.md b/.agents/skills/core-js/SKILL.md
index 77517bf53..4932bd207 100644
--- a/.agents/skills/core-js/SKILL.md
+++ b/.agents/skills/core-js/SKILL.md
@@ -50,7 +50,7 @@ switchesTriggerList.map(function (switchTriggerEl: HTMLElement) {
- The attribute API stays `data-bs-*`. Renaming it to `data-tblr-*` is a breaking change held for 2.0 — do not start it here.
- Keep the accessible state in sync with the class (`aria-pressed`, `aria-expanded`), as above.
- A new module must be imported from `js/tabler.ts`, or it never ships.
-- Shared helpers go to `js/src/tabler.ts` (`prefix`, `hexToRgba`, `getColor`), which reads theme values through `--tblr-*` custom properties.
+- There is no shared colour helper: demo charts resolve `--tblr-*` colours to hex in `shared/components/demo/Chart.astro`.
## 4. Docs markers
diff --git a/.changeset/palette-oklch.md b/.changeset/palette-oklch.md
new file mode 100644
index 000000000..d583a7866
--- /dev/null
+++ b/.changeset/palette-oklch.md
@@ -0,0 +1,6 @@
+---
+"@tabler/core": minor
+"@tabler/docs": patch
+---
+
+Updated the color palette to `oklch()`, masked the checkbox, switch, toggler and carousel icons, and removed the `tabler` JS namespace.
diff --git a/core/js/src/tabler.ts b/core/js/src/tabler.ts
deleted file mode 100644
index 48b60e313..000000000
--- a/core/js/src/tabler.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-export const prefix: string = 'tblr-'
-
-export const hexToRgba = (hex: string, opacity: number): string | null => {
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
-
- return result ? `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${opacity})` : null
-}
-
-export const getColor = (color: string, opacity: number = 1): string | null => {
- const c = getComputedStyle(document.body).getPropertyValue(`--${prefix}${color}`).trim()
-
- if (opacity !== 1) {
- return hexToRgba(c, opacity)
- }
-
- return c
-}
diff --git a/core/js/tabler.ts b/core/js/tabler.ts
index 35d459052..e4822a3f1 100644
--- a/core/js/tabler.ts
+++ b/core/js/tabler.ts
@@ -12,6 +12,3 @@ import './src/sortable'
// Re-export everything from bootstrap.ts (single source of truth)
export * from './src/bootstrap'
-
-// Re-export tabler namespace
-export * as tabler from './src/tabler'
diff --git a/core/package.json b/core/package.json
index 435c80018..6828bfe9a 100644
--- a/core/package.json
+++ b/core/package.json
@@ -178,19 +178,19 @@
},
{
"path": "./dist/css/tabler-themes.css",
- "maxSize": "1.7 kB"
+ "maxSize": "2 kB"
},
{
"path": "./dist/css/tabler-themes.min.css",
- "maxSize": "1.4 kB"
+ "maxSize": "1.6 kB"
},
{
"path": "./dist/css/tabler-themes.rtl.css",
- "maxSize": "1.7 kB"
+ "maxSize": "2 kB"
},
{
"path": "./dist/css/tabler-themes.rtl.min.css",
- "maxSize": "1.4 kB"
+ "maxSize": "1.6 kB"
},
{
"path": "./dist/css/tabler-vendors.css",
diff --git a/core/scss/_settings.scss b/core/scss/_settings.scss
index 7613d835e..22989174f 100644
--- a/core/scss/_settings.scss
+++ b/core/scss/_settings.scss
@@ -9,7 +9,9 @@
// Prefixes
// Characters which are escaped by the escape-svg function
-$escaped-characters: (('<', '%3c'), ('>', '%3e'), ('#', '%23'), ('(', '%28'), (')', '%29')) !default;
+// `%` goes first so the escapes added below are not re-encoded; it shows up in
+// `oklch()` lightness values that end up inside svg data uris.
+$escaped-characters: (('%', '%25'), ('<', '%3c'), ('>', '%3e'), ('#', '%23'), ('(', '%28'), (')', '%29')) !default;
$black: #000 !default;
$white: #fff !default;
diff --git a/core/scss/_variables-dark.scss b/core/scss/_variables-dark.scss
index f9f426839..d4d0c7f1f 100644
--- a/core/scss/_variables-dark.scss
+++ b/core/scss/_variables-dark.scss
@@ -73,9 +73,7 @@ $mark-bg-dark: mix-color-css(var(--black), var(--yellow), 60%) !default;
$form-select-indicator-color-dark: $body-color-dark !default;
$form-select-indicator-dark: url("data:image/svg+xml,") !default;
-// Compile-time `rgba()` on purpose: url-encoded into the svg below, where `var()` cannot resolve.
-$form-switch-color-dark: rgba($white, 0.25) !default;
-$form-switch-bg-image-dark: url("data:image/svg+xml,") !default;
+$form-switch-color-dark: color-transparent(var(--white), 0.25) !default;
// Form validation colors dark
$form-valid-color-dark: mix-color-css(var(--white), var(--green), 40%) !default;
diff --git a/core/scss/_variables.scss b/core/scss/_variables.scss
index 256d72f9e..445f4e0e4 100644
--- a/core/scss/_variables.scss
+++ b/core/scss/_variables.scss
@@ -34,17 +34,17 @@ $enable-important-utilities: true !default;
$color-mode-type: data !default;
// Colors
-$gray-50: #f9fafb !default;
-$gray-100: #f3f4f6 !default;
-$gray-200: #e5e7eb !default;
-$gray-300: #d1d5db !default;
-$gray-400: #9ca3af !default;
-$gray-500: #6b7280 !default;
-$gray-600: #4b5563 !default;
-$gray-700: #374151 !default;
-$gray-800: #1f2937 !default;
-$gray-900: #111827 !default;
-$gray-950: #030712 !default;
+$gray-50: oklch(98.46% 0.0017 247.84deg) !default; // #f9fafb
+$gray-100: oklch(96.7% 0.0029 264.54deg) !default; // #f3f4f6
+$gray-200: oklch(92.76% 0.0058 264.53deg) !default; // #e5e7eb
+$gray-300: oklch(87.17% 0.0093 258.34deg) !default; // #d1d5db
+$gray-400: oklch(71.37% 0.0192 261.32deg) !default; // #9ca3af
+$gray-500: oklch(55.1% 0.0234 264.36deg) !default; // #6b7280
+$gray-600: oklch(44.61% 0.0263 256.8deg) !default; // #4b5563
+$gray-700: oklch(37.29% 0.0306 259.73deg) !default; // #374151
+$gray-800: oklch(27.81% 0.0296 256.85deg) !default; // #1f2937
+$gray-900: oklch(21.01% 0.0318 264.66deg) !default; // #111827
+$gray-950: oklch(12.96% 0.0274 261.69deg) !default; // #030712
// $black and $white moved to `_settings.scss`
@@ -63,18 +63,18 @@ $grays: (
'900': $gray-900,
) !default;
-$blue: #066fd1 !default;
-$azure: #4299e1 !default;
-$indigo: #4263eb !default;
-$purple: #ae3ec9 !default;
-$pink: #d6336c !default;
-$red: #d63939 !default;
-$orange: #f76707 !default;
-$yellow: #f59f00 !default;
-$lime: #74b816 !default;
-$green: #2fb344 !default;
-$teal: #0ca678 !default;
-$cyan: #17a2b8 !default;
+$blue: oklch(54.6% 0.1724 254.2deg) !default; // #066fd1
+$azure: oklch(66.26% 0.1358 246.81deg) !default; // #4299e1
+$indigo: oklch(55.66% 0.2082 268.35deg) !default; // #4263eb
+$purple: oklch(57.83% 0.2186 319.15deg) !default; // #ae3ec9
+$pink: oklch(58.81% 0.201 5.25deg) !default; // #d6336c
+$red: oklch(58.29% 0.1941 25.59deg) !default; // #d63939
+$orange: oklch(68.51% 0.1954 44.57deg) !default; // #f76707
+$yellow: oklch(77.02% 0.1658 71.02deg) !default; // #f59f00
+$lime: oklch(70.99% 0.1897 131.53deg) !default; // #74b816
+$green: oklch(67.38% 0.1885 145.43deg) !default; // #2fb344
+$teal: oklch(64.32% 0.1338 164.7deg) !default; // #0ca678
+$cyan: oklch(65.52% 0.1105 212.17deg) !default; // #17a2b8
$colors: (
'blue': $blue,
@@ -629,17 +629,17 @@ $extra-colors: (
$social-colors: (
'x': #000,
- 'facebook': #1877f2,
+ 'facebook': #0866ff,
'twitter': #1da1f2,
'linkedin': #0a66c2,
- 'google': #dc4e41,
+ 'google': #4285f4,
'youtube': #f00,
'vimeo': #1ab7ea,
'dribbble': #ea4c89,
'github': #181717,
- 'instagram': #e4405f,
+ 'instagram': #ff0069,
'pinterest': #bd081c,
- 'vk': #6383a8,
+ 'vk': #07f,
'rss': #ffa500,
'flickr': #0063dc,
'bitbucket': #0052cc,
@@ -1335,8 +1335,8 @@ $carousel-control-hover-opacity: 0.9 !default;
$carousel-control-transition: opacity 0.15s ease !default;
$carousel-control-icon-filter: null !default;
$carousel-control-icon-width: 1.5rem !default;
-$carousel-control-prev-icon-bg: url("data:image/svg+xml,") !default;
-$carousel-control-next-icon-bg: url("data:image/svg+xml,") !default;
+$carousel-control-prev-icon-bg: url("data:image/svg+xml,") !default;
+$carousel-control-next-icon-bg: url("data:image/svg+xml,") !default;
$carousel-indicator-width: 30px !default;
$carousel-indicator-height: 3px !default;
@@ -1593,11 +1593,9 @@ $navbar-light-active-color: var(--body-color) !default;
$navbar-light-hover-color: var(--body-color) !default;
$navbar-light-disabled-color: var(--disabled-color) !default;
$navbar-light-active-bg: color-transparent(var(--primary), 0.08) !default;
-// Compile-time `rgba()` on purpose: this value is url-encoded into the svg data
-// uri below, and an svg image cannot resolve `var()` or `color-mix()` with
-// `var()` — the stroke would be dropped and the icon would disappear.
-$navbar-light-icon-color: rgba($body-color, 0.75) !default;
-$navbar-light-toggler-icon-bg: url("data:image/svg+xml,") !default;
+$navbar-light-icon-color: color-transparent(var(--body-color), 0.75) !default;
+// Monochrome: painted with `--navbar-toggler-icon-color` through `mask-icon()`.
+$navbar-toggler-icon-bg: url("data:image/svg+xml,") !default;
$navbar-light-toggler-border-color: color-transparent(var(--emphasis-color), 0.15) !default;
$navbar-light-brand-hover-color: $navbar-light-active-color !default;
@@ -1607,9 +1605,7 @@ $navbar-dark-active-bg: color-transparent(var(--white), 0.06) !default;
$navbar-dark-brand-color: $white !default;
$navbar-dark-active-color: $white !default;
$navbar-dark-disabled-color: var(--disabled-color) !default;
-// Compile-time `rgba()` on purpose, see `$navbar-light-icon-color`.
-$navbar-dark-icon-color: rgba($white, $text-secondary-opacity) !default;
-$navbar-dark-toggler-icon-bg: url("data:image/svg+xml,") !default;
+$navbar-dark-icon-color: color-transparent(var(--white), $text-secondary-opacity) !default;
$navbar-dark-toggler-border-color: color-transparent(var(--white), 0.1) !default;
$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;
@@ -1946,15 +1942,16 @@ $form-check-input-box-shadow: $input-box-shadow !default;
$form-check-input-checked-bg-size: 1.25rem !default;
$form-check-input-checked-bg-color: var(--primary) !default;
$form-check-input-checked-bg-repeat: repeat !default;
-$form-check-input-checked-bg-image: url("data:image/svg+xml,") !default;
-$form-check-input-checked-bg-image-dark: url("data:image/svg+xml,") !default;
+$form-check-input-checked-color: $white !default;
+// Monochrome icons, painted with `--form-check-icon-color` through `mask-icon()`.
+$form-check-input-checked-bg-image: url("data:image/svg+xml,") !default;
$form-check-input-checked-border-color: $input-border-color-translucent !default;
$form-check-input-indeterminate-color: $component-active-color !default;
$form-check-input-indeterminate-bg-color: var(--primary) !default;
$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color !default;
-$form-check-input-indeterminate-bg-image: url("data:image/svg+xml,") !default;
+$form-check-input-indeterminate-bg-image: url("data:image/svg+xml,") !default;
-$form-check-radio-checked-bg-image: url("data:image/svg+xml,") !default;
+$form-check-radio-checked-bg-image: url("data:image/svg+xml,") !default;
$form-check-input-disabled-opacity: 0.5 !default;
$form-check-label-disabled-opacity: $text-secondary-opacity;
@@ -1997,19 +1994,17 @@ $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: $gray-200 !default;
+$form-switch-color: var(--gray-200) !default;
$form-switch-width: 2rem !default;
$form-switch-height: 1.25rem !default;
$form-switch-padding-start: $form-switch-width + 0.5rem !default;
-$form-switch-bg-image: url("data:image/svg+xml,") !default;
$form-switch-border-radius: $form-switch-width !default;
-$form-switch-transition: background-position 0.15s ease-in-out !default;
-// Compile-time tint on purpose: url-encoded into the svg below, where `var()` cannot resolve.
-$form-switch-focus-color: tint-color($component-active-bg, 50%) !default;
-$form-switch-focus-bg-image: url("data:image/svg+xml,") !default;
-$form-switch-checked-color: $white !default;
-$form-switch-checked-bg-image: url("data:image/svg+xml,") !default;
-$form-switch-checked-bg-position: right center !default;
+$form-switch-transition:
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out !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-checked-color: var(--white) !default;
// Form range
$form-range-track-width: 100% !default;
diff --git a/core/scss/bootstrap/_carousel.scss b/core/scss/bootstrap/_carousel.scss
index 60df9ce1f..c3f610d4c 100644
--- a/core/scss/bootstrap/_carousel.scss
+++ b/core/scss/bootstrap/_carousel.scss
@@ -130,16 +130,15 @@
display: inline-block;
width: $carousel-control-icon-width;
height: $carousel-control-icon-width;
- background-repeat: no-repeat;
- background-position: 50%;
- background-size: 100% 100%;
+ background-color: currentColor;
+ @include mask-icon($size: 100% 100%, $position: 50%);
}
.carousel-control-prev-icon {
- background-image: escape-svg($carousel-control-prev-icon-bg) #{'/*rtl:' + escape-svg($carousel-control-next-icon-bg) + '*/'};
+ mask-image: escape-svg($carousel-control-prev-icon-bg) #{'/*rtl:' + escape-svg($carousel-control-next-icon-bg) + '*/'};
}
.carousel-control-next-icon {
- background-image: escape-svg($carousel-control-next-icon-bg) #{'/*rtl:' + escape-svg($carousel-control-prev-icon-bg) + '*/'};
+ mask-image: escape-svg($carousel-control-next-icon-bg) #{'/*rtl:' + escape-svg($carousel-control-prev-icon-bg) + '*/'};
}
// Optional indicator pips/controls
diff --git a/core/scss/bootstrap/_navbar.scss b/core/scss/bootstrap/_navbar.scss
index 4e26f6ffe..37456bf09 100644
--- a/core/scss/bootstrap/_navbar.scss
+++ b/core/scss/bootstrap/_navbar.scss
@@ -24,7 +24,8 @@
--navbar-toggler-padding-y: #{$navbar-toggler-padding-y};
--navbar-toggler-padding-x: #{$navbar-toggler-padding-x};
--navbar-toggler-font-size: #{$navbar-toggler-font-size};
- --navbar-toggler-icon-bg: #{escape-svg($navbar-light-toggler-icon-bg)};
+ --navbar-toggler-icon-bg: #{escape-svg($navbar-toggler-icon-bg)};
+ --navbar-toggler-icon-color: #{$navbar-light-icon-color};
--navbar-toggler-border-color: #{$navbar-light-toggler-border-color};
--navbar-toggler-border-radius: #{$navbar-toggler-border-radius};
--navbar-toggler-focus-width: #{$navbar-toggler-focus-width};
@@ -175,10 +176,8 @@
width: 1.5em;
height: 1.5em;
vertical-align: middle;
- background-image: var(--navbar-toggler-icon-bg);
- background-repeat: no-repeat;
- background-position: center;
- background-size: 100%;
+ background-color: var(--navbar-toggler-icon-color);
+ @include mask-icon(var(--navbar-toggler-icon-bg), $size: 100%);
}
.navbar-nav-scroll {
@@ -278,14 +277,14 @@
--navbar-brand-color: #{$navbar-dark-brand-color};
--navbar-brand-hover-color: #{$navbar-dark-brand-hover-color};
--navbar-toggler-border-color: #{$navbar-dark-toggler-border-color};
- --navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
+ --navbar-toggler-icon-color: #{$navbar-dark-icon-color};
// scss-docs-end navbar-dark-css-vars
}
@if $enable-dark-mode {
@include color-mode(dark) {
.navbar-toggler-icon {
- --navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
+ --navbar-toggler-icon-color: #{$navbar-dark-icon-color};
}
}
}
diff --git a/core/scss/bootstrap/_reboot.scss b/core/scss/bootstrap/_reboot.scss
index 77fae89b3..91d6dfd10 100644
--- a/core/scss/bootstrap/_reboot.scss
+++ b/core/scss/bootstrap/_reboot.scss
@@ -54,7 +54,7 @@ body {
text-align: var(--body-text-align);
background-color: var(--body-bg); // 2
-webkit-text-size-adjust: 100%; // 3
- -webkit-tap-highlight-color: rgba($black, 0); // 4
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0); // 4
}
// scss-docs-end reboot-body-rules
diff --git a/core/scss/bootstrap/forms/_form-check.scss b/core/scss/bootstrap/forms/_form-check.scss
index 04e6e12a4..606fa122c 100644
--- a/core/scss/bootstrap/forms/_form-check.scss
+++ b/core/scss/bootstrap/forms/_form-check.scss
@@ -30,7 +30,9 @@
.form-check-input {
--form-check-bg: #{$form-check-input-bg};
+ --form-check-icon-color: #{$form-check-input-checked-color};
+ position: relative;
flex-shrink: 0;
width: $form-check-input-width;
height: $form-check-input-width;
@@ -38,19 +40,32 @@
vertical-align: top;
appearance: none;
background-color: var(--form-check-bg);
- background-image: var(--form-check-bg-image);
- background-repeat: no-repeat;
- background-position: center;
- background-size: contain;
border: $form-check-input-border;
print-color-adjust: exact; // Keep themed appearance for print
@include transition($form-check-transition);
+ // The check, dash or dot is a pseudo-element painted through a CSS mask, so
+ // its colour comes from `--form-check-icon-color` instead of a colour baked
+ // into the svg. Hidden until the input is checked or indeterminate.
+ &::before {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ content: '';
+ background-color: var(--form-check-icon-color);
+ opacity: 0;
+ @include mask-icon(var(--form-check-icon));
+ }
+
&[type='checkbox'] {
+ --form-check-icon: #{escape-svg($form-check-input-checked-bg-image)};
+
@include border-radius($form-check-input-border-radius);
}
&[type='radio'] {
+ --form-check-icon: #{escape-svg($form-check-radio-checked-bg-image)};
+
// stylelint-disable-next-line property-disallowed-list
border-radius: $form-check-radio-border-radius;
}
@@ -69,31 +84,20 @@
background-color: $form-check-input-checked-bg-color;
border-color: $form-check-input-checked-border-color;
- &[type='checkbox'] {
- @if $enable-gradients {
- --form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)}, var(--gradient);
- } @else {
- --form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)};
- }
- }
-
- &[type='radio'] {
- @if $enable-gradients {
- --form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)}, var(--gradient);
- } @else {
- --form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)};
- }
+ &::before {
+ opacity: 1;
}
}
&[type='checkbox']:indeterminate {
+ --form-check-icon: #{escape-svg($form-check-input-indeterminate-bg-image)};
+ --form-check-icon-color: #{$form-check-input-indeterminate-color};
+
background-color: $form-check-input-indeterminate-bg-color;
border-color: $form-check-input-indeterminate-border-color;
- @if $enable-gradients {
- --form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)}, var(--gradient);
- } @else {
- --form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)};
+ &::before {
+ opacity: 1;
}
}
@@ -127,26 +131,40 @@
padding-inline-start: $form-switch-padding-start;
.form-check-input {
- --form-switch-bg: #{escape-svg($form-switch-bg-image)};
+ --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(#{$form-switch-height} * 0.75);
+ --form-switch-knob-offset: calc(#{$form-switch-height} * 0.125);
width: $form-switch-width;
margin-inline-start: $form-switch-padding-start * -1;
- background-image: var(--form-switch-bg);
- background-position: left center;
@include border-radius($form-switch-border-radius, 0);
@include transition($form-switch-transition);
+ // The knob replaces the masked check icon: a plain circle that slides.
+ &::before {
+ inset: 0 auto 0 var(--form-switch-knob-offset);
+ width: var(--form-switch-knob-size);
+ height: var(--form-switch-knob-size);
+ margin-block: auto;
+ background-color: var(--form-switch-color);
+ // stylelint-disable-next-line property-disallowed-list
+ border-radius: 50%;
+ opacity: 1;
+ mask-image: none;
+ @include transition($form-switch-knob-transition);
+ }
+
&:focus {
- --form-switch-bg: #{escape-svg($form-switch-focus-bg-image)};
+ --form-switch-color: #{$form-switch-focus-color};
}
&:checked {
- background-position: $form-switch-checked-bg-position;
+ --form-switch-color: #{$form-switch-checked-color};
- @if $enable-gradients {
- --form-switch-bg: #{escape-svg($form-switch-checked-bg-image)}, var(--gradient);
- } @else {
- --form-switch-bg: #{escape-svg($form-switch-checked-bg-image)};
+ &::before {
+ inset-inline-start: calc(100% - var(--form-switch-knob-size) - var(--form-switch-knob-offset));
}
}
}
@@ -185,7 +203,7 @@
@if $enable-dark-mode {
@include color-mode(dark) {
.form-switch .form-check-input:not(:checked):not(:focus) {
- --form-switch-bg: #{escape-svg($form-switch-bg-image-dark)};
+ --form-switch-color: #{$form-switch-color-dark};
}
}
}
diff --git a/core/scss/layout/_navbar.scss b/core/scss/layout/_navbar.scss
index 6912f8b1e..7ea1dfd87 100644
--- a/core/scss/layout/_navbar.scss
+++ b/core/scss/layout/_navbar.scss
@@ -504,6 +504,7 @@ Navbar toggler
width: 1.25em;
height: 2px;
background: currentColor;
+ mask-image: none; // the bars below replace the masked hamburger from bootstrap/_navbar
@include border-radius(var(--border-radius-pill));
@include transition(top $navbar-toggler-animation-time $navbar-toggler-animation-time, bottom $navbar-toggler-animation-time $navbar-toggler-animation-time, transform $navbar-toggler-animation-time, opacity 0s $navbar-toggler-animation-time);
diff --git a/core/scss/marketing/_hero.scss b/core/scss/marketing/_hero.scss
index 99895c81b..ec2fa5993 100644
--- a/core/scss/marketing/_hero.scss
+++ b/core/scss/marketing/_hero.scss
@@ -49,8 +49,6 @@
max-width: 65rem;
margin: 4rem auto;
@include border-radius($border-radius-lg);
- //box-shadow: 0 10px 15px -3px rgba($color-text, 0.1),
- // 0 4px 6px -2px rgba($color-text, 0.05);
img,
svg {
@@ -60,12 +58,3 @@
height: auto;
}
}
-//
-//.hero-img-side {
-// img,
-// svg {
-// max-width: 100%;
-// height: auto;
-// display: block;
-// }
-//}
diff --git a/core/scss/mixins/_functions.scss b/core/scss/mixins/_functions.scss
index 6884b498a..c2ce2ea00 100644
--- a/core/scss/mixins/_functions.scss
+++ b/core/scss/mixins/_functions.scss
@@ -35,38 +35,6 @@
@return oklch(from #{$color} calc(l #{$sign} #{math.abs($delta)}) c h);
}
-// str-replace was defined here twice — the canonical copy lives in the
-// "Bootstrap functions" section below.
-
-@mixin media-breakpoint-down-than($name, $breakpoints: $grid-breakpoints) {
- $prev: breakpoint-prev($name, $breakpoints);
-
- @if $prev == null {
- @content;
- } @else {
- $max: breakpoint-max($prev, $breakpoints);
-
- @if $max {
- @media (max-width: $max) {
- @content;
- }
- } @else {
- @content;
- }
- }
-}
-
-@function breakpoint-prev($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map.keys($breakpoints)) {
- $n: list.index($breakpoint-names, $name);
- @if not $n {
- @error "breakpoint `#{$name}` not found in `#{$breakpoints}`";
- }
- @return if(sass($n > 1): list.nth($breakpoint-names, $n - 1); else: null);
-}
-
-// escape-svg was defined here twice — the canonical copy lives in the
-// "Bootstrap functions" section below.
-
/**
* Converts a given value to a percentage string.
*
@@ -98,47 +66,18 @@
@return color-mix(in oklab, #{$color} #{to-percentage($alpha)}, $background);
}
-@function url-svg($svg) {
- $svg: str-replace($svg, '#', '%23');
- $svg: str-replace($svg, '