mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 05:06:55 +04:00
Define the color palette in oklch() (#3043)
This commit is contained in:
1 parent
0fe02b3684
commit
107e493275
28 files changed
+224
-463
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
}
|
||||
@@ -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'
|
||||
+4
-4
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$form-select-indicator-color-dark}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>") !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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-color-dark}'/></svg>") !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;
|
||||
|
||||
+45
-50
@@ -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,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{$carousel-control-color}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='15 18 9 12 15 6'></polyline></svg>") !default;
|
||||
$carousel-control-next-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{$carousel-control-color}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='9 18 15 12 9 6'></polyline></svg>") !default;
|
||||
$carousel-control-prev-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='15 18 9 12 15 6'></polyline></svg>") !default;
|
||||
$carousel-control-next-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='9 18 15 12 9 6'></polyline></svg>") !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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='#{$navbar-light-icon-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='black' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='#{$navbar-dark-icon-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='#{$white}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/></svg>") !default;
|
||||
$form-check-input-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='#{$body-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/></svg>") !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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/></svg>") !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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='#{$form-check-input-indeterminate-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/></svg>") !default;
|
||||
$form-check-input-indeterminate-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/></svg>") !default;
|
||||
|
||||
$form-check-radio-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><circle r='3' fill='#{$white}' cx='8' cy='8' /></svg>") !default;
|
||||
$form-check-radio-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><circle r='3' fill='black' cx='8' cy='8' /></svg>") !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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-color}'/></svg>") !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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-focus-color}'/></svg>") !default;
|
||||
$form-switch-checked-color: $white !default;
|
||||
$form-switch-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-checked-color}'/></svg>") !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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
//}
|
||||
@@ -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, '<svg', '<svg xmlns="http://www.w3.org/2000/svg"');
|
||||
|
||||
@return url('data:image/svg+xml;charset=UTF-8,#{$svg}');
|
||||
// Paints a monochrome svg data uri (authored with `black`) in a Sass colour, for
|
||||
// the few places that cannot use `mask-icon()`: a pseudo-element that already
|
||||
// carries a background, or a form control without pseudo-elements. Pass a Sass
|
||||
// colour, never `var()` — an svg image cannot resolve custom properties.
|
||||
@function svg-icon-color($svg, $color) {
|
||||
@return str-replace($svg, "'black'", "'#{$color}'");
|
||||
}
|
||||
|
||||
// Bootstrap functions
|
||||
//
|
||||
// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.
|
||||
|
||||
// Ascending
|
||||
// Used to evaluate Sass maps like our grid breakpoints.
|
||||
@mixin _assert-ascending($map, $map-name) {
|
||||
$prev-key: null;
|
||||
$prev-num: null;
|
||||
@each $key, $num in $map {
|
||||
@if $prev-num == null or math.unit($num) == '%' or math.unit($prev-num) == '%' {
|
||||
// Do nothing
|
||||
} @else if not math.compatible($prev-num, $num) {
|
||||
@warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
|
||||
} @else if $prev-num >= $num {
|
||||
@warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
|
||||
}
|
||||
$prev-key: $key;
|
||||
$prev-num: $num;
|
||||
}
|
||||
}
|
||||
|
||||
// Starts at zero
|
||||
// Used to ensure the min-width of the lowest breakpoint starts at 0.
|
||||
@mixin _assert-starts-at-zero($map, $map-name: '$grid-breakpoints') {
|
||||
@if list.length($map) > 0 {
|
||||
$values: map.values($map);
|
||||
$first-value: list.nth($values, 1);
|
||||
@if $first-value != 0 {
|
||||
@warn "First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Colors
|
||||
@function to-rgb($value) {
|
||||
@return color.channel($value, 'red', $space: rgb), color.channel($value, 'green', $space: rgb), color.channel($value, 'blue', $space: rgb);
|
||||
@@ -177,14 +116,6 @@
|
||||
}
|
||||
// stylelint-enable scss/dollar-variable-pattern
|
||||
|
||||
@function varify($list) {
|
||||
$result: null;
|
||||
@each $entry in $list {
|
||||
$result: list.append($result, var(--#{$entry}), space);
|
||||
}
|
||||
@return $result;
|
||||
}
|
||||
|
||||
// Internal Bootstrap function to turn maps into its negative variant.
|
||||
// It prefixes the keys with `n` and makes the value negative.
|
||||
@function negativify-map($map) {
|
||||
@@ -197,32 +128,6 @@
|
||||
@return $result;
|
||||
}
|
||||
|
||||
// Get multiple keys from a sass map
|
||||
@function map-get-multiple($map, $values) {
|
||||
$result: ();
|
||||
@each $key, $value in $map {
|
||||
@if (list.index($values, $key) != null) {
|
||||
$result: map.merge(
|
||||
$result,
|
||||
(
|
||||
$key: $value,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@return $result;
|
||||
}
|
||||
|
||||
// Merge multiple maps
|
||||
@function map-merge-multiple($maps...) {
|
||||
$merged-maps: ();
|
||||
|
||||
@each $map in $maps {
|
||||
$merged-maps: map.merge($merged-maps, $map);
|
||||
}
|
||||
@return $merged-maps;
|
||||
}
|
||||
|
||||
// Replace `$search` with `$replace` in `$string`
|
||||
// Used on our SVG icon backgrounds for custom forms.
|
||||
//
|
||||
@@ -325,7 +230,7 @@ $_luminance-list: 0.0008 0.001 0.0011 0.0013 0.0015 0.0017 0.002 0.0022 0.0025 0
|
||||
// Return opaque color
|
||||
// opaque(#fff, rgba(0, 0, 0, .5)) => #808080
|
||||
@function opaque($background, $foreground) {
|
||||
@return color.mix(rgba($foreground, 1), $background, color.opacity($foreground) * 100%);
|
||||
@return color.mix(color.change($foreground, $alpha: 1), $background, color.channel($foreground, 'alpha') * 100%, $method: srgb);
|
||||
}
|
||||
|
||||
// Tint a color: mix a color with white (in oklab, see `mix-color()`)
|
||||
|
||||
@@ -79,6 +79,23 @@
|
||||
//
|
||||
// Print styles
|
||||
//
|
||||
// Renders an svg icon through a CSS mask so the shape is painted with the
|
||||
// element's `background-color` and follows the theme without a colour baked
|
||||
// into the data uri. Set `background-color` on the element; pass `null` for
|
||||
// `$icon` when the mask image is applied per state.
|
||||
@mixin mask-icon($icon: null, $size: contain, $position: center) {
|
||||
@if $icon != null {
|
||||
mask-image: $icon;
|
||||
}
|
||||
mask-repeat: no-repeat;
|
||||
@if $position != null {
|
||||
mask-position: $position;
|
||||
}
|
||||
@if $size != null {
|
||||
mask-size: $size;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin media-print {
|
||||
@media print {
|
||||
@content;
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
[data-bs-theme-base='slate'],
|
||||
[data-theme-base='slate'] {
|
||||
--gray-50: #f8fafc;
|
||||
--gray-100: #f1f5f9;
|
||||
--gray-200: #e2e8f0;
|
||||
--gray-300: #cbd5e1;
|
||||
--gray-400: #94a3b8;
|
||||
--gray-500: #64748b;
|
||||
--gray-600: #475569;
|
||||
--gray-700: #334155;
|
||||
--gray-800: #1e293b;
|
||||
--gray-900: #0f172a;
|
||||
--gray-950: #020617;
|
||||
--gray-50: oklch(98.42% 0.0034 247.86deg); // #f8fafc
|
||||
--gray-100: oklch(96.83% 0.0069 247.9deg); // #f1f5f9
|
||||
--gray-200: oklch(92.88% 0.0126 255.51deg); // #e2e8f0
|
||||
--gray-300: oklch(86.9% 0.0198 252.89deg); // #cbd5e1
|
||||
--gray-400: oklch(71.07% 0.0351 256.79deg); // #94a3b8
|
||||
--gray-500: oklch(55.44% 0.0407 257.42deg); // #64748b
|
||||
--gray-600: oklch(44.55% 0.0374 257.28deg); // #475569
|
||||
--gray-700: oklch(37.17% 0.0392 257.29deg); // #334155
|
||||
--gray-800: oklch(27.95% 0.0368 260.03deg); // #1e293b
|
||||
--gray-900: oklch(20.77% 0.0398 265.75deg); // #0f172a
|
||||
--gray-950: oklch(12.88% 0.0406 264.7deg); // #020617
|
||||
}
|
||||
|
||||
[data-bs-theme-base='gray'],
|
||||
@@ -32,63 +32,63 @@
|
||||
|
||||
[data-bs-theme-base='zinc'],
|
||||
[data-theme-base='zinc'] {
|
||||
--gray-50: #fafafa;
|
||||
--gray-100: #f4f4f5;
|
||||
--gray-200: #e4e4e7;
|
||||
--gray-300: #d4d4d8;
|
||||
--gray-400: #a1a1aa;
|
||||
--gray-500: #71717a;
|
||||
--gray-600: #52525b;
|
||||
--gray-700: #3f3f46;
|
||||
--gray-800: #27272a;
|
||||
--gray-900: #18181b;
|
||||
--gray-950: #09090b;
|
||||
--gray-50: oklch(98.51% 0 0deg); // #fafafa
|
||||
--gray-100: oklch(96.74% 0.0013 286.37deg); // #f4f4f5
|
||||
--gray-200: oklch(91.97% 0.004 286.32deg); // #e4e4e7
|
||||
--gray-300: oklch(87.11% 0.0055 286.29deg); // #d4d4d8
|
||||
--gray-400: oklch(71.18% 0.0129 286.07deg); // #a1a1aa
|
||||
--gray-500: oklch(55.17% 0.0138 285.94deg); // #71717a
|
||||
--gray-600: oklch(44.19% 0.0146 285.79deg); // #52525b
|
||||
--gray-700: oklch(37.03% 0.0119 285.81deg); // #3f3f46
|
||||
--gray-800: oklch(27.39% 0.0055 286.03deg); // #27272a
|
||||
--gray-900: oklch(21.03% 0.0059 285.89deg); // #18181b
|
||||
--gray-950: oklch(14.08% 0.0044 285.82deg); // #09090b
|
||||
}
|
||||
|
||||
[data-bs-theme-base='neutral'],
|
||||
[data-theme-base='neutral'] {
|
||||
--gray-50: #fafafa;
|
||||
--gray-100: #f5f5f5;
|
||||
--gray-200: #e5e5e5;
|
||||
--gray-300: #d4d4d4;
|
||||
--gray-400: #a3a3a3;
|
||||
--gray-500: #737373;
|
||||
--gray-600: #525252;
|
||||
--gray-700: #404040;
|
||||
--gray-800: #262626;
|
||||
--gray-900: #171717;
|
||||
--gray-950: #0a0a0a;
|
||||
--gray-50: oklch(98.51% 0 0deg); // #fafafa
|
||||
--gray-100: oklch(97.02% 0 0deg); // #f5f5f5
|
||||
--gray-200: oklch(92.19% 0 0deg); // #e5e5e5
|
||||
--gray-300: oklch(86.99% 0 0deg); // #d4d4d4
|
||||
--gray-400: oklch(71.55% 0 0deg); // #a3a3a3
|
||||
--gray-500: oklch(55.55% 0 0deg); // #737373
|
||||
--gray-600: oklch(43.86% 0 0deg); // #525252
|
||||
--gray-700: oklch(37.15% 0 0deg); // #404040
|
||||
--gray-800: oklch(26.86% 0 0deg); // #262626
|
||||
--gray-900: oklch(20.46% 0 0deg); // #171717
|
||||
--gray-950: oklch(14.48% 0 0deg); // #0a0a0a
|
||||
}
|
||||
|
||||
[data-bs-theme-base='stone'],
|
||||
[data-theme-base='stone'] {
|
||||
--gray-50: #fafaf9;
|
||||
--gray-100: #f5f5f4;
|
||||
--gray-200: #e7e5e4;
|
||||
--gray-300: #d6d3d1;
|
||||
--gray-400: #a8a29e;
|
||||
--gray-500: #78716c;
|
||||
--gray-600: #57534e;
|
||||
--gray-700: #44403c;
|
||||
--gray-800: #292524;
|
||||
--gray-900: #1c1917;
|
||||
--gray-950: #0c0a09;
|
||||
--gray-50: oklch(98.48% 0.0013 106.42deg); // #fafaf9
|
||||
--gray-100: oklch(96.99% 0.0013 106.42deg); // #f5f5f4
|
||||
--gray-200: oklch(92.32% 0.0026 48.72deg); // #e7e5e4
|
||||
--gray-300: oklch(86.87% 0.0043 56.37deg); // #d6d3d1
|
||||
--gray-400: oklch(71.61% 0.0091 56.26deg); // #a8a29e
|
||||
--gray-500: oklch(55.34% 0.0116 58.07deg); // #78716c
|
||||
--gray-600: oklch(44.44% 0.0096 73.64deg); // #57534e
|
||||
--gray-700: oklch(37.41% 0.0087 67.56deg); // #44403c
|
||||
--gray-800: oklch(26.85% 0.0063 34.3deg); // #292524
|
||||
--gray-900: oklch(21.61% 0.0061 56.04deg); // #1c1917
|
||||
--gray-950: oklch(14.69% 0.0041 49.25deg); // #0c0a09
|
||||
}
|
||||
|
||||
// Easter egg: not listed in the theme settings panel, enable with `data-bs-theme-base="pink"` or `?theme-base=pink`
|
||||
[data-bs-theme-base='pink'],
|
||||
[data-theme-base='pink'] {
|
||||
--gray-50: #fdf2f8;
|
||||
--gray-100: #fce7f3;
|
||||
--gray-200: #fbcfe8;
|
||||
--gray-300: #f9a8d4;
|
||||
--gray-400: #f472b6;
|
||||
--gray-500: #ec4899;
|
||||
--gray-600: #db2777;
|
||||
--gray-700: #be185d;
|
||||
--gray-800: #9d174d;
|
||||
--gray-900: #831843;
|
||||
--gray-950: #500724;
|
||||
--gray-50: oklch(97.14% 0.0141 343.2deg); // #fdf2f8
|
||||
--gray-100: oklch(94.82% 0.0276 342.26deg); // #fce7f3
|
||||
--gray-200: oklch(89.94% 0.0589 343.23deg); // #fbcfe8
|
||||
--gray-300: oklch(82.28% 0.1095 346.02deg); // #f9a8d4
|
||||
--gray-400: oklch(72.53% 0.1752 349.76deg); // #f472b6
|
||||
--gray-500: oklch(65.59% 0.2118 354.31deg); // #ec4899
|
||||
--gray-600: oklch(59.16% 0.218 0.58deg); // #db2777
|
||||
--gray-700: oklch(52.46% 0.199 3.96deg); // #be185d
|
||||
--gray-800: oklch(45.87% 0.1697 3.82deg); // #9d174d
|
||||
--gray-900: oklch(40.78% 0.1442 2.43deg); // #831843
|
||||
--gray-950: oklch(28.45% 0.1048 3.91deg); // #500724
|
||||
}
|
||||
|
||||
@each $name, $value in $extra-colors {
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
// "needs config" tier: they default to the global `$grid-breakpoints`, but every
|
||||
// function also accepts an explicit `$breakpoints` map, so the tests pass a
|
||||
// local fixture map and stay independent of the project variables.
|
||||
//
|
||||
// `breakpoint-prev` lives in mixins/_functions.scss; the rest live in
|
||||
// mixins/bootstrap/_breakpoints.scss.
|
||||
|
||||
@use '../mixins/functions' as fn;
|
||||
@use '../mixins/bootstrap/breakpoints' as *;
|
||||
|
||||
$grid: (
|
||||
@@ -17,15 +13,13 @@ $grid: (
|
||||
xl: 1200px,
|
||||
);
|
||||
|
||||
@include describe('breakpoint-next / breakpoint-prev') {
|
||||
@include describe('breakpoint-next') {
|
||||
@include it('returns the neighbouring breakpoint name') {
|
||||
@include assert-equal(breakpoint-next(md, $grid), lg);
|
||||
@include assert-equal(fn.breakpoint-prev(md, $grid), sm);
|
||||
}
|
||||
|
||||
@include it('returns null past the last / before the first breakpoint') {
|
||||
@include it('returns null past the last breakpoint') {
|
||||
@include assert-equal(breakpoint-next(xl, $grid), null);
|
||||
@include assert-equal(fn.breakpoint-prev(xs, $grid), null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,16 +67,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include describe('url-svg') {
|
||||
@include it("percent-encodes '#' so the data URI stays valid") {
|
||||
@include assert-equal(meta.inspect(url-svg('#')), 'url("data:image/svg+xml;charset=UTF-8,%23")');
|
||||
}
|
||||
|
||||
@include it('injects the SVG xmlns attribute') {
|
||||
@include assert-equal(meta.inspect(url-svg('<svg></svg>')), 'url(\'data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg"></svg>\')');
|
||||
}
|
||||
}
|
||||
|
||||
@include describe('map-loop') {
|
||||
// Applies a module-level function (resolved by name via meta.get-function) to
|
||||
// every value in the map. `to-percentage` lives in the same module, so it
|
||||
|
||||
@@ -1,31 +1,10 @@
|
||||
// Tests for the CSS custom-property builders in scss/mixins/_functions.scss:
|
||||
// `varify()` and `rgba-css-var()`. A plain `@use` of the functions module is
|
||||
// enough — no fixtures. Both emit unprefixed names: the `--tblr-` prefix is
|
||||
// added later, by the css pipeline (.build/css-var-prefix.ts).
|
||||
// Tests for `rgba-css-var()` in scss/mixins/_functions.scss. A plain `@use` of
|
||||
// the functions module is enough — no fixtures. It emits unprefixed names: the
|
||||
// `--tblr-` prefix is added later, by the css pipeline (.build/css-var-prefix.ts).
|
||||
|
||||
@use 'sass:meta';
|
||||
@use '../mixins/functions' as *;
|
||||
|
||||
@include describe('varify') {
|
||||
// `varify` seeds its accumulator with `null`, so the raw list inspects as
|
||||
// `null var(...) ...`; Sass drops the null when serialising to CSS, so the
|
||||
// behaviour is asserted through the rendered output rather than the value.
|
||||
@include it('prefixes each entry as a space-separated var() list') {
|
||||
@include assert() {
|
||||
@include output() {
|
||||
.t {
|
||||
padding: varify(('gutter-x' 'gutter-y'));
|
||||
}
|
||||
}
|
||||
@include expect() {
|
||||
.t {
|
||||
padding: var(--gutter-x) var(--gutter-y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include describe('rgba-css-var') {
|
||||
@include it('builds a color-mix() from the identifier and target opacity') {
|
||||
@include assert-equal(meta.inspect(rgba-css-var('primary', 'bg')), 'color-mix(in oklab, var(--primary) calc(var(--bg-opacity) * 100%), transparent)');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@use 'sass:meta';
|
||||
// Tests for `escape-svg()` from scss/mixins/_functions.scss.
|
||||
//
|
||||
// It iterates the global `$escaped-characters` map, which lives in
|
||||
@@ -8,6 +9,10 @@
|
||||
@use '../mixins/functions' as *;
|
||||
|
||||
@include describe('escape-svg') {
|
||||
@include it('percent-encodes % so oklch() lightness survives inside a data uri') {
|
||||
@include assert-equal(meta.inspect(escape-svg('url("data:image/svg+xml,<path stroke=\'oklch(54.6% 0.17 254deg)\'/>")')), 'url("data:image/svg+xml,%3cpath stroke=\'oklch%2854.6%25 0.17 254deg%29\'/%3e")');
|
||||
}
|
||||
|
||||
@include it('returns a non data-URI string unchanged') {
|
||||
@include assert-equal(escape-svg('no data uri <here>'), 'no data uri <here>');
|
||||
}
|
||||
|
||||
@@ -33,44 +33,3 @@
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@include describe('map-get-multiple') {
|
||||
@include it('keeps only the requested keys, preserving map order') {
|
||||
@include assert-equal(
|
||||
map-get-multiple(
|
||||
(
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
),
|
||||
(a c)
|
||||
),
|
||||
(
|
||||
a: 1,
|
||||
c: 3,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@include describe('map-merge-multiple') {
|
||||
@include it('merges maps left-to-right, later keys winning') {
|
||||
@include assert-equal(
|
||||
map-merge-multiple(
|
||||
(
|
||||
a: 1,
|
||||
),
|
||||
(
|
||||
b: 2,
|
||||
),
|
||||
(
|
||||
a: 3,
|
||||
)
|
||||
),
|
||||
(
|
||||
a: 3,
|
||||
b: 2,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,6 @@
|
||||
// functions, every mixin accepts an explicit `$breakpoints` map, so the tests
|
||||
// pass a local fixture map and stay independent of the global `$grid-breakpoints`.
|
||||
//
|
||||
// `media-breakpoint-{up,down,between,only}` come from
|
||||
// mixins/bootstrap/_breakpoints.scss; `media-breakpoint-down-than` lives in
|
||||
// mixins/_functions.scss. Their names don't collide, so both are used as `*`.
|
||||
//
|
||||
// Max-widths are the breakpoint value minus 0.02px (the min-/max- overlap fix);
|
||||
// the smallest/edge cases emit the content with no media query at all.
|
||||
|
||||
@@ -137,66 +133,3 @@ $g: (
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include describe('media-breakpoint-down-than') {
|
||||
@include it('targets everything below the previous breakpoint') {
|
||||
@include assert() {
|
||||
@include output() {
|
||||
@include media-breakpoint-down-than(md, $g) {
|
||||
.t {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
@include expect() {
|
||||
@media (max-width: 575.98px) {
|
||||
.t {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include it('emits content directly when there is no previous breakpoint') {
|
||||
@include assert() {
|
||||
@include output() {
|
||||
@include media-breakpoint-down-than(xs, $g) {
|
||||
.t {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
@include expect() {
|
||||
.t {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Names that don't exist in the global $grid-breakpoints at all: this only
|
||||
// resolves if $breakpoints actually reaches the breakpoint-prev() lookup,
|
||||
// not just the breakpoint-max() one.
|
||||
$custom: (
|
||||
tablet: 0,
|
||||
desktop: 900px,
|
||||
);
|
||||
|
||||
@include it('resolves the previous breakpoint from a custom $breakpoints map') {
|
||||
@include assert() {
|
||||
@include output() {
|
||||
@include media-breakpoint-down-than(desktop, $custom) {
|
||||
.t {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
@include expect() {
|
||||
.t {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ Color Input
|
||||
height: 100%;
|
||||
content: '';
|
||||
background: no-repeat center center/$form-check-input-checked-bg-size;
|
||||
background-image: escape-svg($form-check-input-checked-bg-image);
|
||||
background-image: escape-svg(svg-icon-color($form-check-input-checked-bg-image, $form-check-input-checked-color));
|
||||
opacity: 0;
|
||||
@include transition(opacity $transition-time);
|
||||
|
||||
@@ -51,7 +51,7 @@ Color Input
|
||||
|
||||
@at-root .form-colorinput-light & {
|
||||
&::before {
|
||||
background-image: escape-svg($form-check-input-checked-bg-image-dark);
|
||||
background-image: escape-svg(svg-icon-color($form-check-input-checked-bg-image, $body-color));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ Image check
|
||||
|
||||
.form-imagecheck-input:checked ~ & {
|
||||
background-color: $form-check-input-checked-bg-color;
|
||||
background-image: escape-svg($form-check-input-checked-bg-image);
|
||||
background-image: escape-svg(svg-icon-color($form-check-input-checked-bg-image, $form-check-input-checked-color));
|
||||
background-repeat: $form-check-input-checked-bg-repeat;
|
||||
background-position: center;
|
||||
background-size: $form-check-input-checked-bg-size;
|
||||
@@ -66,7 +66,7 @@ Image check
|
||||
}
|
||||
|
||||
.form-imagecheck-input[type='radio']:checked ~ & {
|
||||
background-image: escape-svg($form-check-radio-checked-bg-image);
|
||||
background-image: escape-svg(svg-icon-color($form-check-radio-checked-bg-image, $form-check-input-checked-color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,11 +85,11 @@ Select group
|
||||
}
|
||||
|
||||
.form-selectgroup-input[type='checkbox']:checked + .form-selectgroup-label & {
|
||||
background-image: escape-svg($form-check-input-checked-bg-image);
|
||||
background-image: escape-svg(svg-icon-color($form-check-input-checked-bg-image, $form-check-input-checked-color));
|
||||
}
|
||||
|
||||
.form-selectgroup-input[type='radio']:checked + .form-selectgroup-label & {
|
||||
background-image: escape-svg($form-check-radio-checked-bg-image);
|
||||
background-image: escape-svg(svg-icon-color($form-check-radio-checked-bg-image, $form-check-input-checked-color));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,13 +103,7 @@ Coloris({
|
||||
})
|
||||
```
|
||||
|
||||
To read the colors from the theme instead of writing them by hand, take them from the CSS variables:
|
||||
|
||||
```js
|
||||
const swatches = ['--tblr-blue', '--tblr-azure', '--tblr-indigo'].map(function (prop) {
|
||||
return getComputedStyle(document.body).getPropertyValue(prop)
|
||||
})
|
||||
```
|
||||
Write the swatches as hex values. Tabler's CSS variables such as `--tblr-blue` hold `oklch()` colors, which Coloris cannot parse, so do not read the swatches from them.
|
||||
|
||||
### Swatches only
|
||||
|
||||
@@ -192,9 +186,7 @@ Use the picker like any other form field: a label, the input, and a hint below i
|
||||
window.addEventListener('load', function () {
|
||||
if (typeof Coloris === 'undefined') return;
|
||||
|
||||
const swatches = ['--tblr-blue', '--tblr-azure', '--tblr-indigo', '--tblr-purple', '--tblr-pink', '--tblr-red', '--tblr-orange', '--tblr-green'].map(function (prop) {
|
||||
return getComputedStyle(document.body).getPropertyValue(prop).trim();
|
||||
});
|
||||
const swatches = ['#066fd1', '#4299e1', '#4263eb', '#ae3ec9', '#d6336c', '#d63939', '#f76707', '#2fb344'];
|
||||
|
||||
Coloris({ el: '.form-control[id^="colorpicker"]', selectInput: false, format: 'hex', alpha: false, swatchesOnly: false, swatches: swatches });
|
||||
|
||||
|
||||
@@ -15,8 +15,10 @@ interface Props {
|
||||
const { value, id, format = false, class: className, ariaLabel } = Astro.props
|
||||
const alpha = false
|
||||
|
||||
const colors = site.colors as Record<string, { prop: string }>
|
||||
const swatchProps = Object.values(colors).map((color) => color.prop)
|
||||
// Coloris parses hex, rgb and hsl only, and the CSS tokens are `oklch()`, so the
|
||||
// swatches use the hex values from site.json.
|
||||
const colors = site.colors as Record<string, { hex: string }>
|
||||
const swatches = Object.values(colors).map((color) => color.hex)
|
||||
|
||||
const colorpickerId = `colorpicker-${id}`
|
||||
const colorpickerSelector = `#colorpicker-${id}`
|
||||
@@ -25,12 +27,10 @@ const colorpickerSelector = `#colorpicker-${id}`
|
||||
<input type="text" class={`form-control d-block${className ? ` ${className}` : ''}`} id={colorpickerId} value={value} aria-label={ariaLabel ?? 'Color value'} />
|
||||
<CaptureScript>
|
||||
<!-- BEGIN COLORPICKER -->
|
||||
<script is:inline define:vars={{ colorpickerId, colorpickerSelector, alpha, format, swatchProps }}>
|
||||
<script is:inline define:vars={{ colorpickerId, colorpickerSelector, alpha, format, swatches }}>
|
||||
function initColorpicker() {
|
||||
window.tabler_colorpicker ??= {}
|
||||
|
||||
const swatches = swatchProps.map((prop) => window.getComputedStyle(document.body).getPropertyValue(prop))
|
||||
|
||||
if (window.Coloris) {
|
||||
// `el` can only be configured globally, but Coloris.setInstance() scopes the rest
|
||||
// (alpha, format, swatches, ...) to this selector so multiple pickers with
|
||||
|
||||
Reference in new issue
Block a user