diff --git a/.changeset/form-check-tokens.md b/.changeset/form-check-tokens.md new file mode 100644 index 000000000..ecbc2fc43 --- /dev/null +++ b/.changeset/form-check-tokens.md @@ -0,0 +1,6 @@ +--- +"@tabler/core": minor +"@tabler/docs": patch +--- + +Added `--tblr-form-check-*` and `--tblr-form-switch-*` variables, with shared `--tblr-control-checked-bg` and disabled tokens on `:root`. diff --git a/.changeset/form-control-tokens.md b/.changeset/form-control-tokens.md new file mode 100644 index 000000000..26f53bc97 --- /dev/null +++ b/.changeset/form-control-tokens.md @@ -0,0 +1,6 @@ +--- +"@tabler/core": minor +"@tabler/docs": patch +--- + +Added `--tblr-control-*` variables to `.form-control` and `.form-select`; sizes and the file button now follow them. diff --git a/.changeset/form-partials-tokens.md b/.changeset/form-partials-tokens.md new file mode 100644 index 000000000..1374b5d14 --- /dev/null +++ b/.changeset/form-partials-tokens.md @@ -0,0 +1,5 @@ +--- +"@tabler/core": minor +--- + +Updated icon addons, select groups, `.col-form-label`, validation icons, OTP and Tom Select to read the `--tblr-control-*` and `--tblr-btn-input-*` variables. diff --git a/.changeset/form-select-size-height.md b/.changeset/form-select-size-height.md new file mode 100644 index 000000000..b330e6d40 --- /dev/null +++ b/.changeset/form-select-size-height.md @@ -0,0 +1,5 @@ +--- +"@tabler/core": minor +--- + +Fixed `.form-select-sm` and `.form-select-lg` height to match `.form-control-sm` and `.form-control-lg`. Small buttons, inputs and selects are now 32px tall instead of 28px. Fixed the font size of a small Tom Select to match a small native select. diff --git a/.changeset/forms-css-variables-docs.md b/.changeset/forms-css-variables-docs.md new file mode 100644 index 000000000..ed44ce533 --- /dev/null +++ b/.changeset/forms-css-variables-docs.md @@ -0,0 +1,5 @@ +--- +"@tabler/docs": patch +--- + +Added the Forms → CSS variables page: the control scale, state colors and every `--tblr-control-*`, add-on, floating, range, check and switch token. diff --git a/.changeset/input-group-floating-range-tokens.md b/.changeset/input-group-floating-range-tokens.md new file mode 100644 index 000000000..536895b8a --- /dev/null +++ b/.changeset/input-group-floating-range-tokens.md @@ -0,0 +1,6 @@ +--- +"@tabler/core": minor +"@tabler/docs": patch +--- + +Added `--tblr-input-group-addon-*`, `--tblr-form-floating-*` and `--tblr-range-*` variables to input groups, floating labels and range inputs. diff --git a/core/package.json b/core/package.json index 37e580c94..8a181cc92 100644 --- a/core/package.json +++ b/core/package.json @@ -148,7 +148,7 @@ }, { "path": "./dist/css/tabler-props.css", - "maxSize": "4.2 kB" + "maxSize": "4.3 kB" }, { "path": "./dist/css/tabler-props.min.css", @@ -156,7 +156,7 @@ }, { "path": "./dist/css/tabler-props.rtl.css", - "maxSize": "4.2 kB" + "maxSize": "4.3 kB" }, { "path": "./dist/css/tabler-props.rtl.min.css", diff --git a/core/scss/_deprecated.scss b/core/scss/_deprecated.scss index 5b58ba37c..4f200eff6 100644 --- a/core/scss/_deprecated.scss +++ b/core/scss/_deprecated.scss @@ -52,6 +52,13 @@ } } + // deprecated(2.0): `--form-select-bg-img` is the old name of `--control-select-bg` + // (from `bootstrap/forms/_form-select.scss`). Still read by `.form-select`, + // so a project that set it keeps its arrow. + .form-select { + --form-select-bg-img: var(--control-select-bg); + } + // `.form-hint` is deprecated, use `.form-text` instead // (from `bootstrap/forms/_form-text.scss`). .form-hint { diff --git a/core/scss/_props.scss b/core/scss/_props.scss index a5f8854c9..02841ac08 100644 --- a/core/scss/_props.scss +++ b/core/scss/_props.scss @@ -125,6 +125,14 @@ --btn-input-xl-min-height: calc(var(--btn-input-xl-line-height) + var(--btn-input-xl-padding-y) * 2 + #{$input-btn-border-width} * 2); // scss-docs-end btn-input-css-vars + /** Checks, radios and switches: shared state colors, in the names Bootstrap 6 uses */ + // scss-docs-start control-state-css-vars + --control-checked-bg: #{$form-check-input-checked-bg-color}; + --control-checked-border-color: #{$form-check-input-checked-border-color}; + --control-disabled-bg: #{$input-disabled-bg}; + --control-disabled-opacity: #{$form-check-input-disabled-opacity}; + // scss-docs-end control-state-css-vars + /** Backdrops */ --backdrop-opacity: #{$backdrop-opacity}; --backdrop-bg: #{$backdrop-bg}; diff --git a/core/scss/_variables.scss b/core/scss/_variables.scss index caa133799..b6273def6 100644 --- a/core/scss/_variables.scss +++ b/core/scss/_variables.scss @@ -1063,7 +1063,7 @@ $badge-padding-x-lg: 0.5rem !default; $input-btn-border-width: var(--border-width) !default; $input-btn-font-family: var(--body-font-family) !default; -$input-btn-padding-y-sm: 0.3125rem !default; +$input-btn-padding-y-sm: 0.4375rem !default; $input-btn-padding-x-sm: 0.5rem !default; $input-btn-font-size-sm: $h5-font-size !default; $input-btn-line-height-sm: 1rem !default; diff --git a/core/scss/bootstrap/forms/_floating-labels.scss b/core/scss/bootstrap/forms/_floating-labels.scss index 0a1c1a2ac..7c80f3c01 100644 --- a/core/scss/bootstrap/forms/_floating-labels.scss +++ b/core/scss/bootstrap/forms/_floating-labels.scss @@ -1,14 +1,33 @@ @use '../../config' as *; .form-floating { + // scss-docs-start form-floating-css-vars + --form-floating-height: #{$form-floating-height}; + --form-floating-line-height: #{$form-floating-line-height}; + --form-floating-padding-x: #{var-if-default($form-floating-padding-x, $input-btn-padding-x, '--btn-input-padding-x')}; + --form-floating-padding-y: #{$form-floating-padding-y}; + --form-floating-input-padding-t: #{$form-floating-input-padding-t}; + --form-floating-input-padding-b: #{$form-floating-input-padding-b}; + --form-floating-label-height: #{$form-floating-label-height}; + // The textarea label masks the text under it. It is a sibling of the control, + // so it cannot read the control's `--control-bg` and mirrors the scale instead. + --form-floating-label-bg: #{var-if-default($input-bg, var(--bg-forms), '--btn-input-bg')}; + --form-floating-label-disabled-bg: #{$input-disabled-bg}; + --form-floating-label-border-radius: #{var-if-default($input-border-radius, var(--border-radius), '--btn-input-border-radius')}; + --form-floating-label-opacity: #{$form-floating-label-opacity}; + --form-floating-label-transform: #{$form-floating-label-transform}; + --form-floating-label-disabled-color: #{$form-floating-label-disabled-color}; + --form-floating-transition: #{$form-floating-transition}; + // scss-docs-end form-floating-css-vars + position: relative; > .form-control, > .form-control-plaintext, > .form-select { - height: $form-floating-height; - min-height: $form-floating-height; - line-height: $form-floating-line-height; + height: var(--form-floating-height); + min-height: var(--form-floating-height); + line-height: var(--form-floating-line-height); } > label { @@ -18,21 +37,21 @@ z-index: 2; max-width: 100%; height: 100%; // allow textareas - padding: $form-floating-padding-y $form-floating-padding-x; + padding: var(--form-floating-padding-y) var(--form-floating-padding-x); overflow: hidden; - color: color-transparent(var(--body-color), $form-floating-label-opacity); + color: color-transparent(var(--body-color), calc(var(--form-floating-label-opacity) * 100%)); text-align: start; text-overflow: ellipsis; white-space: nowrap; pointer-events: none; border: $input-border-width solid transparent; // Required for aligning label's text with the input as it affects inner box model transform-origin: 0 0; - @include transition($form-floating-transition); + @include transition(var(--form-floating-transition)); } > .form-control, > .form-control-plaintext { - padding: $form-floating-padding-y $form-floating-padding-x; + padding: var(--form-floating-padding-y) var(--form-floating-padding-x); &::placeholder { color: transparent; @@ -40,20 +59,20 @@ &:focus, &:not(:placeholder-shown) { - padding-top: $form-floating-input-padding-t; - padding-bottom: $form-floating-input-padding-b; + padding-top: var(--form-floating-input-padding-t); + padding-bottom: var(--form-floating-input-padding-b); } // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped &:-webkit-autofill { - padding-top: $form-floating-input-padding-t; - padding-bottom: $form-floating-input-padding-b; + padding-top: var(--form-floating-input-padding-t); + padding-bottom: var(--form-floating-input-padding-b); } } > .form-select { - padding-top: $form-floating-input-padding-t; - padding-bottom: $form-floating-input-padding-b; - padding-inline-start: $form-floating-padding-x; + padding-top: var(--form-floating-input-padding-t); + padding-bottom: var(--form-floating-input-padding-b); + padding-inline-start: var(--form-floating-padding-x); } > .form-control:focus, @@ -61,29 +80,29 @@ > .form-control-plaintext, > .form-select { ~ label { - transform: $form-floating-label-transform; + transform: var(--form-floating-label-transform); } } // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped > .form-control:-webkit-autofill { ~ label { - transform: $form-floating-label-transform; + transform: var(--form-floating-label-transform); } } > textarea:focus, > textarea:not(:placeholder-shown) { ~ label::after { position: absolute; - inset: $form-floating-padding-y ($form-floating-padding-x * 0.5); + inset: var(--form-floating-padding-y) calc(var(--form-floating-padding-x) * 0.5); z-index: -1; - height: $form-floating-label-height; + height: var(--form-floating-label-height); content: ''; - background-color: $input-bg; - @include border-radius($input-border-radius); + background-color: var(--form-floating-label-bg); + @include border-radius(var(--form-floating-label-border-radius)); } } > textarea:disabled ~ label::after { - background-color: $input-disabled-bg; + background-color: var(--form-floating-label-disabled-bg); } > .form-control-plaintext { @@ -95,6 +114,6 @@ > :disabled ~ label, > .form-control:disabled ~ label { // Required for `.form-control`s because of specificity - color: $form-floating-label-disabled-color; + color: var(--form-floating-label-disabled-color); } } diff --git a/core/scss/bootstrap/forms/_form-check.scss b/core/scss/bootstrap/forms/_form-check.scss index a527fa9eb..320bdd0d5 100644 --- a/core/scss/bootstrap/forms/_form-check.scss +++ b/core/scss/bootstrap/forms/_form-check.scss @@ -5,44 +5,67 @@ @use '../../config' as *; .form-check { + // scss-docs-start form-check-css-vars + --form-check-min-height: #{$form-check-min-height}; + --form-check-padding-start: #{$form-check-padding-start}; + --form-check-margin-bottom: #{$form-check-margin-bottom}; + // scss-docs-end form-check-css-vars + display: block; - min-height: $form-check-min-height; - padding-inline-start: $form-check-padding-start; - margin-bottom: $form-check-margin-bottom; + min-height: var(--form-check-min-height); + padding-inline-start: var(--form-check-padding-start); + margin-bottom: var(--form-check-margin-bottom); .form-check-input { float: inline-start; - margin-inline-start: $form-check-padding-start * -1; + margin-inline-start: calc(var(--form-check-padding-start) * -1); } } .form-check-reverse { - padding-inline-end: $form-check-padding-start; + padding-inline-end: var(--form-check-padding-start); padding-inline-start: 0; text-align: end; .form-check-input { float: inline-end; - margin-inline-end: $form-check-padding-start * -1; + margin-inline-end: calc(var(--form-check-padding-start) * -1); margin-inline-start: 0; } } +// The checked, indeterminate and disabled tokens read the shared state colors +// on `:root` (`--control-checked-bg` and friends) until the Sass variable is +// configured, so one override retunes checks, radios and switches together. .form-check-input { + // scss-docs-start form-check-input-css-vars + --form-check-input-width: #{$form-check-input-width}; --form-check-bg: #{$form-check-input-bg}; + --form-check-border: #{$form-check-input-border}; + --form-check-border-radius: #{$form-check-input-border-radius}; + --form-check-box-shadow: #{$form-check-input-box-shadow}; --form-check-icon-color: #{$form-check-input-checked-color}; + --form-check-focus-border-color: #{$form-check-input-focus-border}; + --form-check-active-filter: #{$form-check-input-active-filter}; + --form-check-checked-bg: #{var-if-default($form-check-input-checked-bg-color, var(--primary), '--control-checked-bg')}; + --form-check-checked-border-color: #{var-if-default($form-check-input-checked-border-color, var(--border-color-translucent), '--control-checked-border-color')}; + --form-check-indeterminate-bg: #{var-if-default($form-check-input-indeterminate-bg-color, var(--primary), '--control-checked-bg')}; + --form-check-indeterminate-border-color: #{var-if-default($form-check-input-indeterminate-border-color, var(--primary), '--form-check-indeterminate-bg')}; + --form-check-disabled-opacity: #{var-if-default($form-check-input-disabled-opacity, 0.5, '--control-disabled-opacity')}; + --form-check-transition: #{$form-check-transition}; + // scss-docs-end form-check-input-css-vars position: relative; flex-shrink: 0; - width: $form-check-input-width; - height: $form-check-input-width; + width: var(--form-check-input-width); + height: var(--form-check-input-width); margin-top: ($line-height-base - $form-check-input-width) * 0.5; // line-height minus check height vertical-align: top; appearance: none; background-color: var(--form-check-bg); - border: $form-check-input-border; + border: var(--form-check-border); print-color-adjust: exact; // Keep themed appearance for print - @include transition($form-check-transition); + @include transition(var(--form-check-transition)); // Start the focus ring's `outline-color` transition from transparent, not `currentcolor`. outline-color: transparent; @@ -62,7 +85,7 @@ &[type='checkbox'] { --form-check-icon: #{escape-svg($form-check-input-checked-bg-image)}; - @include border-radius($form-check-input-border-radius); + @include border-radius(var(--form-check-border-radius)); } &[type='radio'] { @@ -73,17 +96,17 @@ } &:active { - filter: $form-check-input-active-filter; + filter: var(--form-check-active-filter); } &:focus-visible { - border-color: $form-check-input-focus-border; + border-color: var(--form-check-focus-border-color); @include focus-ring(true); } &:checked { - background-color: $form-check-input-checked-bg-color; - border-color: $form-check-input-checked-border-color; + background-color: var(--form-check-checked-bg); + border-color: var(--form-check-checked-border-color); &::before { opacity: 1; @@ -94,8 +117,8 @@ --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; + background-color: var(--form-check-indeterminate-bg); + border-color: var(--form-check-indeterminate-border-color); &::before { opacity: 1; @@ -105,7 +128,7 @@ &:disabled { pointer-events: none; filter: none; - opacity: $form-check-input-disabled-opacity; + opacity: var(--form-check-disabled-opacity); } // Use disabled attribute in addition of :disabled pseudo-class @@ -129,18 +152,29 @@ // .form-switch { - padding-inline-start: $form-switch-padding-start; + // scss-docs-start form-switch-css-vars + --form-switch-width: #{$form-switch-width}; + --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-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')}; + --form-switch-disabled-color: #{$form-switch-disabled-color}; + // scss-docs-end form-switch-css-vars + + padding-inline-start: var(--form-switch-padding-start); .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(#{$form-switch-height} * 0.75); - --form-switch-knob-offset: calc(#{$form-switch-height} * 0.125); + --form-switch-knob-size: calc(var(--form-switch-height) * 0.75); + --form-switch-knob-offset: calc(var(--form-switch-height) * 0.125); - width: $form-switch-width; - margin-inline-start: $form-switch-padding-start * -1; - @include border-radius($form-switch-border-radius, 0); + width: var(--form-switch-width); + margin-inline-start: calc(var(--form-switch-padding-start) * -1); + @include border-radius(var(--form-switch-border-radius), 0); @include transition($form-switch-transition); // The knob replaces the masked check icon: a plain circle that slides. @@ -159,11 +193,11 @@ } &:focus { - --form-switch-color: #{$form-switch-focus-color}; + --form-switch-color: var(--form-switch-focus-color); } &:checked { - --form-switch-color: #{$form-switch-checked-color}; + --form-switch-color: var(--form-switch-checked-color); &::before { inset-inline-start: calc(100% - var(--form-switch-knob-size) - var(--form-switch-knob-offset)); @@ -172,11 +206,11 @@ } &.form-check-reverse { - padding-inline-end: $form-switch-padding-start; + padding-inline-end: var(--form-switch-padding-start); padding-inline-start: 0; .form-check-input { - margin-inline-end: $form-switch-padding-start * -1; + margin-inline-end: calc(var(--form-switch-padding-start) * -1); margin-inline-start: 0; } } diff --git a/core/scss/bootstrap/forms/_form-control.scss b/core/scss/bootstrap/forms/_form-control.scss index cf92336d8..584a60072 100644 --- a/core/scss/bootstrap/forms/_form-control.scss +++ b/core/scss/bootstrap/forms/_form-control.scss @@ -6,25 +6,57 @@ @use '../../config' as *; +// The control tokens, in the names Bootstrap 6 uses. One block serves every +// text-like control: `.form-select` and `.form-control-plaintext` stand alone +// (no `.form-control`), so they need the same declarations. A token seeded from +// a size variable reads the `:root` scale until the Sass variable is configured. +.form-select, +.form-control-plaintext, +.form-control { + // scss-docs-start control-css-vars + --control-min-height: calc(var(--control-line-height) + var(--control-padding-y) * 2 + var(--control-border-width) * 2); + --control-padding-y: #{var-if-default($input-padding-y, $input-btn-padding-y, '--btn-input-padding-y')}; + --control-padding-x: #{var-if-default($input-padding-x, $input-btn-padding-x, '--btn-input-padding-x')}; + @include rfs(var-if-default($input-font-size, $input-btn-font-size, '--btn-input-font-size'), --control-font-size); + --control-line-height: #{var-if-default($input-line-height, $input-btn-line-height, '--btn-input-line-height')}; + --control-fg: #{var-if-default($input-color, var(--body-color), '--btn-input-fg')}; + --control-bg: #{var-if-default($input-bg, var(--bg-forms), '--btn-input-bg')}; + --control-border-width: #{$input-border-width}; + --control-border-color: #{$input-border-color}; + --control-border-radius: #{var-if-default($input-border-radius, var(--border-radius), '--btn-input-border-radius')}; + --control-box-shadow: #{$input-box-shadow}; + --control-focus-fg: #{var-if-default($input-focus-color, $input-color, '--control-fg')}; + --control-focus-bg: #{var-if-default($input-focus-bg, $input-bg, '--control-bg')}; + --control-focus-border-color: #{$input-focus-border-color}; + --control-placeholder-color: #{$input-placeholder-color}; + --control-disabled-color: #{if(sass($input-disabled-color): $input-disabled-color; else: $form-secondary-color)}; + --control-disabled-bg: #{$input-disabled-bg}; + --control-disabled-border-color: #{if(sass($input-disabled-border-color): $input-disabled-border-color; else: var(--control-border-color))}; + --control-action-bg: #{$form-file-button-bg}; + --control-action-hover-bg: #{$form-file-button-hover-bg}; + --control-transition: #{$input-transition}; + // scss-docs-end control-css-vars +} + .form-control { display: block; width: 100%; - padding: $input-padding-y $input-padding-x; + padding: var(--control-padding-y) var(--control-padding-x); font-family: $input-font-family; - @include font-size($input-font-size); + @include font-size(var(--control-font-size)); font-weight: $input-font-weight; - line-height: $input-line-height; - color: $input-color; + line-height: var(--control-line-height); + color: var(--control-fg); appearance: none; // Fix appearance for date inputs in Safari - background-color: $input-bg; + background-color: var(--control-bg); background-clip: padding-box; - border: $input-border-width solid $input-border-color; + border: var(--control-border-width) solid var(--control-border-color); // Note: This has no effect on `s in CSS. - @include border-radius($input-border-radius, 0); + @include border-radius(var(--control-border-radius), 0); - @include box-shadow($input-box-shadow); - @include transition($input-transition); + @include box-shadow(var(--control-box-shadow)); + @include transition(var(--control-transition)); // Start the focus ring's `outline-color` transition from transparent, not `currentcolor`. outline-color: transparent; @@ -38,9 +70,9 @@ // Customize the `:focus` state to imitate native WebKit styles. &:focus { - color: $input-focus-color; - background-color: $input-focus-bg; - border-color: $input-focus-border-color; + color: var(--control-focus-fg); + background-color: var(--control-focus-bg); + border-color: var(--control-focus-border-color); --focus-ring-offset: -1px; @include focus-ring(true); } @@ -74,7 +106,7 @@ // Placeholder &::placeholder { - color: $input-placeholder-color; + color: var(--control-placeholder-color); // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526. opacity: 1; } @@ -85,31 +117,31 @@ // disabled if the fieldset is disabled. Due to implementation difficulty, we // don't honor that edge case; we style them as disabled anyway. &:disabled { - color: $input-disabled-color; - background-color: $input-disabled-bg; - border-color: $input-disabled-border-color; + color: var(--control-disabled-color); + background-color: var(--control-disabled-bg); + border-color: var(--control-disabled-border-color); // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655. opacity: 1; } // File input buttons theming &::file-selector-button { - padding: $input-padding-y $input-padding-x; - margin: (-$input-padding-y) (-$input-padding-x); - margin-inline-end: $input-padding-x; + padding: var(--control-padding-y) var(--control-padding-x); + margin: calc(var(--control-padding-y) * -1) calc(var(--control-padding-x) * -1); + margin-inline-end: var(--control-padding-x); color: $form-file-button-color; - @include gradient-bg($form-file-button-bg); + @include gradient-bg(var(--control-action-bg)); pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; - border-inline-end-width: $input-border-width; + border-inline-end-width: var(--control-border-width); border-radius: 0; // stylelint-disable-line property-disallowed-list @include transition($btn-transition); } &:hover:not(:disabled):not([readonly])::file-selector-button { - background-color: $form-file-button-hover-bg; + background-color: var(--control-action-hover-bg); } } @@ -121,13 +153,13 @@ .form-control-plaintext { display: block; width: 100%; - padding: $input-padding-y 0; + padding: var(--control-padding-y) 0; margin-bottom: 0; // match inputs if this class comes on inputs with default margins - line-height: $input-line-height; - color: $input-plaintext-color; + line-height: var(--control-line-height); + color: #{var-if-default($input-plaintext-color, $input-color, '--control-fg')}; background-color: transparent; border: solid transparent; - border-width: $input-border-width 0; + border-width: var(--control-border-width) 0; &:focus { outline: 0; @@ -143,36 +175,29 @@ // Form control sizing // // Build on `.form-control` with modifier classes to decrease or increase the -// height and font-size of form controls. +// height and font-size of form controls. A size only moves the tokens; the +// height, the file button and the color swatch follow. // // Repeated in `_input_group.scss` to avoid Sass extend issues. .form-control-sm { - line-height: $input-line-height-sm; - min-height: $input-height-sm; - padding: $input-padding-y-sm $input-padding-x-sm; - @include font-size($input-font-size-sm); - @include border-radius($input-border-radius-sm); - - &::file-selector-button { - padding: $input-padding-y-sm $input-padding-x-sm; - margin: (-$input-padding-y-sm) (-$input-padding-x-sm); - margin-inline-end: $input-padding-x-sm; - } + min-height: var(--control-min-height); + @include font-size(var(--control-font-size)); + --control-padding-y: #{var-if-default($input-padding-y-sm, $input-btn-padding-y-sm, '--btn-input-sm-padding-y')}; + --control-padding-x: #{var-if-default($input-padding-x-sm, $input-btn-padding-x-sm, '--btn-input-sm-padding-x')}; + @include rfs(var-if-default($input-font-size-sm, $input-btn-font-size-sm, '--btn-input-sm-font-size'), --control-font-size); + --control-line-height: #{var-if-default($input-line-height-sm, $input-btn-line-height-sm, '--btn-input-sm-line-height')}; + --control-border-radius: #{var-if-default($input-border-radius-sm, var(--border-radius-sm), '--btn-input-sm-border-radius')}; } .form-control-lg { - line-height: $input-line-height-lg; - min-height: $input-height-lg; - padding: $input-padding-y-lg $input-padding-x-lg; - @include font-size($input-font-size-lg); - @include border-radius($input-border-radius-lg); - - &::file-selector-button { - padding: $input-padding-y-lg $input-padding-x-lg; - margin: (-$input-padding-y-lg) (-$input-padding-x-lg); - margin-inline-end: $input-padding-x-lg; - } + min-height: var(--control-min-height); + @include font-size(var(--control-font-size)); + --control-padding-y: #{var-if-default($input-padding-y-lg, $input-btn-padding-y-lg, '--btn-input-lg-padding-y')}; + --control-padding-x: #{var-if-default($input-padding-x-lg, $input-btn-padding-x-lg, '--btn-input-lg-padding-x')}; + @include rfs(var-if-default($input-font-size-lg, $input-btn-font-size-lg, '--btn-input-lg-font-size'), --control-font-size); + --control-line-height: #{var-if-default($input-line-height-lg, $input-btn-line-height-lg, '--btn-input-lg-line-height')}; + --control-border-radius: #{var-if-default($input-border-radius-lg, var(--border-radius-lg), '--btn-input-lg-border-radius')}; } // Make sure textareas don't shrink too much when resized @@ -180,15 +205,7 @@ // stylelint-disable selector-no-qualifying-type textarea { &.form-control { - min-height: $input-height; - } - - &.form-control-sm { - min-height: $input-height-sm; - } - - &.form-control-lg { - min-height: $input-height-lg; + min-height: var(--control-min-height); } } @@ -196,8 +213,8 @@ textarea { .form-control-color { width: $form-color-width; - height: $input-height; - padding: $input-padding-y; + height: var(--control-min-height); + padding: var(--control-padding-y); &:not(:disabled):not([readonly]) { cursor: pointer; @@ -205,19 +222,11 @@ textarea { &::-moz-color-swatch { border: 0 !important; // stylelint-disable-line declaration-no-important - @include border-radius($input-border-radius); + @include border-radius(var(--control-border-radius)); } &::-webkit-color-swatch { border: 0 !important; // stylelint-disable-line declaration-no-important - @include border-radius($input-border-radius); - } - - &.form-control-sm { - height: $input-height-sm; - } - - &.form-control-lg { - height: $input-height-lg; + @include border-radius(var(--control-border-radius)); } } diff --git a/core/scss/bootstrap/forms/_form-range.scss b/core/scss/bootstrap/forms/_form-range.scss index 86477ede0..44d911c2e 100644 --- a/core/scss/bootstrap/forms/_form-range.scss +++ b/core/scss/bootstrap/forms/_form-range.scss @@ -7,8 +7,26 @@ @use '../../config' as *; .form-range { + // scss-docs-start range-css-vars + --range-track-width: #{$form-range-track-width}; + --range-track-height: #{$form-range-track-height}; + --range-track-cursor: #{$form-range-track-cursor}; + --range-track-bg: #{$form-range-track-bg}; + --range-track-border-radius: #{$form-range-track-border-radius}; + --range-track-box-shadow: #{$form-range-track-box-shadow}; + --range-thumb-width: #{$form-range-thumb-width}; + --range-thumb-height: #{$form-range-thumb-height}; + --range-thumb-bg: #{$form-range-thumb-bg}; + --range-thumb-border: #{$form-range-thumb-border}; + --range-thumb-border-radius: #{$form-range-thumb-border-radius}; + --range-thumb-box-shadow: #{$form-range-thumb-box-shadow}; + --range-thumb-active-bg: #{$form-range-thumb-active-bg}; + --range-thumb-disabled-bg: #{$form-range-thumb-disabled-bg}; + --range-thumb-transition: #{$form-range-thumb-transition}; + // scss-docs-end range-css-vars + width: 100%; - height: add($form-range-thumb-height, $form-range-thumb-focus-box-shadow-width * 2); + height: calc(var(--range-thumb-height) + #{$form-range-thumb-focus-box-shadow-width * 2}); padding: 0; // Need to reset padding appearance: none; background-color: transparent; @@ -31,69 +49,69 @@ } &::-webkit-slider-thumb { - width: $form-range-thumb-width; - height: $form-range-thumb-height; - margin-top: ($form-range-track-height - $form-range-thumb-height) * 0.5; // Webkit specific + width: var(--range-thumb-width); + height: var(--range-thumb-height); + margin-top: calc((var(--range-track-height) - var(--range-thumb-height)) / 2); // Webkit specific appearance: none; - @include gradient-bg($form-range-thumb-bg); - border: $form-range-thumb-border; - @include border-radius($form-range-thumb-border-radius); - @include box-shadow($form-range-thumb-box-shadow); - @include transition($form-range-thumb-transition); + @include gradient-bg(var(--range-thumb-bg)); + border: var(--range-thumb-border); + @include border-radius(var(--range-thumb-border-radius)); + @include box-shadow(var(--range-thumb-box-shadow)); + @include transition(var(--range-thumb-transition)); outline-color: transparent; &:active { - @include gradient-bg($form-range-thumb-active-bg); + @include gradient-bg(var(--range-thumb-active-bg)); } } &::-webkit-slider-runnable-track { - width: $form-range-track-width; - height: $form-range-track-height; + width: var(--range-track-width); + height: var(--range-track-height); color: transparent; // Why? - cursor: $form-range-track-cursor; - background-color: $form-range-track-bg; + cursor: var(--range-track-cursor); + background-color: var(--range-track-bg); border-color: transparent; - @include border-radius($form-range-track-border-radius); - @include box-shadow($form-range-track-box-shadow); + @include border-radius(var(--range-track-border-radius)); + @include box-shadow(var(--range-track-box-shadow)); } &::-moz-range-thumb { - width: $form-range-thumb-width; - height: $form-range-thumb-height; + width: var(--range-thumb-width); + height: var(--range-thumb-height); appearance: none; - @include gradient-bg($form-range-thumb-bg); - border: $form-range-thumb-border; - @include border-radius($form-range-thumb-border-radius); - @include box-shadow($form-range-thumb-box-shadow); - @include transition($form-range-thumb-transition); + @include gradient-bg(var(--range-thumb-bg)); + border: var(--range-thumb-border); + @include border-radius(var(--range-thumb-border-radius)); + @include box-shadow(var(--range-thumb-box-shadow)); + @include transition(var(--range-thumb-transition)); outline-color: transparent; &:active { - @include gradient-bg($form-range-thumb-active-bg); + @include gradient-bg(var(--range-thumb-active-bg)); } } &::-moz-range-track { - width: $form-range-track-width; - height: $form-range-track-height; + width: var(--range-track-width); + height: var(--range-track-height); color: transparent; - cursor: $form-range-track-cursor; - background-color: $form-range-track-bg; + cursor: var(--range-track-cursor); + background-color: var(--range-track-bg); border-color: transparent; // Firefox specific? - @include border-radius($form-range-track-border-radius); - @include box-shadow($form-range-track-box-shadow); + @include border-radius(var(--range-track-border-radius)); + @include box-shadow(var(--range-track-box-shadow)); } &:disabled { pointer-events: none; &::-webkit-slider-thumb { - background-color: $form-range-thumb-disabled-bg; + background-color: var(--range-thumb-disabled-bg); } &::-moz-range-thumb { - background-color: $form-range-thumb-disabled-bg; + background-color: var(--range-thumb-disabled-bg); } } } diff --git a/core/scss/bootstrap/forms/_form-select.scss b/core/scss/bootstrap/forms/_form-select.scss index cf57dbf43..c4a7929b0 100644 --- a/core/scss/bootstrap/forms/_form-select.scss +++ b/core/scss/bootstrap/forms/_form-select.scss @@ -5,75 +5,111 @@ @use '../../config' as *; +// The `--control-*` tokens come from the shared block in `_form-control.scss`. +// A `$form-select-*` variable defaults to its `$input-*` twin; only a value a +// project changed on its own has to be written again here. +@mixin -select-token($token, $value, $default) { + @if $value != $default { + #{$token}: #{$value}; + } +} + .form-select { - --form-select-bg-img: #{escape-svg($form-select-indicator)}; + // scss-docs-start form-select-css-vars + --control-select-bg: #{escape-svg($form-select-indicator)}; + --control-select-bg-dark: #{escape-svg($form-select-indicator-dark)}; + --control-select-bg-position: #{$form-select-bg-position}; + --control-select-bg-size: #{$form-select-bg-size}; + // Room for the arrow. Reads the root scale, not `--control-padding-x`, so the + // size modifiers keep the same 3rem they always had. + --control-select-indicator-padding: #{if(sass($form-select-indicator-padding == $form-select-padding-x * 3 and $form-select-padding-x == $input-btn-padding-x): calc(var(--btn-input-padding-x) * 3); else: $form-select-indicator-padding)}; + // scss-docs-end form-select-css-vars + @include -select-token(--control-padding-y, $form-select-padding-y, $input-padding-y); + @include -select-token(--control-padding-x, $form-select-padding-x, $input-padding-x); + @include -select-token(--control-font-size, $form-select-font-size, $input-font-size); + @include -select-token(--control-line-height, $form-select-line-height, $input-line-height); + @include -select-token(--control-fg, $form-select-color, $input-color); + @include -select-token(--control-bg, $form-select-bg, $input-bg); + @include -select-token(--control-border-width, $form-select-border-width, $input-border-width); + @include -select-token(--control-border-color, $form-select-border-color, $input-border-color); + @include -select-token(--control-border-radius, $form-select-border-radius, $input-border-radius); + @include -select-token(--control-box-shadow, $form-select-box-shadow, $input-box-shadow); + @include -select-token(--control-focus-border-color, $form-select-focus-border-color, $input-focus-border-color); + @include -select-token(--control-disabled-color, $form-select-disabled-color, $input-disabled-color); + @include -select-token(--control-disabled-bg, $form-select-disabled-bg, $input-disabled-bg); + @include -select-token(--control-disabled-border-color, $form-select-disabled-border-color, $input-disabled-border-color); + @include -select-token(--control-transition, $form-select-transition, $input-transition); display: block; width: 100%; - padding: $form-select-padding-y $form-select-indicator-padding $form-select-padding-y $form-select-padding-x; + padding: var(--control-padding-y) var(--control-padding-x); + padding-inline-end: var(--control-select-indicator-padding); font-family: $form-select-font-family; - @include font-size($form-select-font-size); + @include font-size(var(--control-font-size)); font-weight: $form-select-font-weight; - line-height: $form-select-line-height; - color: $form-select-color; + line-height: var(--control-line-height); + color: var(--control-fg); appearance: none; - background-color: $form-select-bg; - background-image: var(--form-select-bg-img), var(--form-select-bg-icon, none); + background-color: var(--control-bg); + // deprecated(2.0): `--form-select-bg-img` is the old name of `--control-select-bg` + background-image: var(--form-select-bg-img, var(--control-select-bg)), var(--form-select-bg-icon, none); background-repeat: no-repeat; - background-position: $form-select-bg-position; - background-size: $form-select-bg-size; - border: $form-select-border-width solid $form-select-border-color; - @include border-radius($form-select-border-radius, 0); - @include box-shadow($form-select-box-shadow); - @include transition($form-select-transition); + background-position: var(--control-select-bg-position); + background-size: var(--control-select-bg-size); + border: var(--control-border-width) solid var(--control-border-color); + @include border-radius(var(--control-border-radius), 0); + @include box-shadow(var(--control-box-shadow)); + @include transition(var(--control-transition)); // Start the focus ring's `outline-color` transition from transparent, not `currentcolor`. outline-color: transparent; &:focus { - border-color: $form-select-focus-border-color; + border-color: var(--control-focus-border-color); --focus-ring-offset: -1px; @include focus-ring(true); } &[multiple], &[size]:not([size='1']) { - padding-inline-end: $form-select-padding-x; + padding-inline-end: var(--control-padding-x); background-image: none; } &:disabled { - color: $form-select-disabled-color; - background-color: $form-select-disabled-bg; - border-color: $form-select-disabled-border-color; + color: var(--control-disabled-color); + background-color: var(--control-disabled-bg); + border-color: var(--control-disabled-border-color); } // Remove outline from select box in FF &:-moz-focusring { color: transparent; - text-shadow: 0 0 0 $form-select-color; + text-shadow: 0 0 0 var(--control-fg); } } +// A size moves the same tokens as `.form-control-sm/-lg`, line height +// included, so a select and an input of one size are equally tall. .form-select-sm { - padding-top: $form-select-padding-y-sm; - padding-bottom: $form-select-padding-y-sm; - padding-inline-start: $form-select-padding-x-sm; - @include font-size($form-select-font-size-sm); - @include border-radius($form-select-border-radius-sm); + --control-padding-y: #{var-if-default($form-select-padding-y-sm, $input-btn-padding-y-sm, '--btn-input-sm-padding-y')}; + --control-padding-x: #{var-if-default($form-select-padding-x-sm, $input-btn-padding-x-sm, '--btn-input-sm-padding-x')}; + @include rfs(var-if-default($form-select-font-size-sm, $input-btn-font-size-sm, '--btn-input-sm-font-size'), --control-font-size); + --control-line-height: #{var-if-default($input-line-height-sm, $input-btn-line-height-sm, '--btn-input-sm-line-height')}; + --control-border-radius: #{var-if-default($form-select-border-radius-sm, var(--border-radius-sm), '--btn-input-sm-border-radius')}; } .form-select-lg { - padding-top: $form-select-padding-y-lg; - padding-bottom: $form-select-padding-y-lg; - padding-inline-start: $form-select-padding-x-lg; - @include font-size($form-select-font-size-lg); - @include border-radius($form-select-border-radius-lg); + --control-padding-y: #{var-if-default($form-select-padding-y-lg, $input-btn-padding-y-lg, '--btn-input-lg-padding-y')}; + --control-padding-x: #{var-if-default($form-select-padding-x-lg, $input-btn-padding-x-lg, '--btn-input-lg-padding-x')}; + @include rfs(var-if-default($form-select-font-size-lg, $input-btn-font-size-lg, '--btn-input-lg-font-size'), --control-font-size); + --control-line-height: #{var-if-default($input-line-height-lg, $input-btn-line-height-lg, '--btn-input-lg-line-height')}; + --control-border-radius: #{var-if-default($form-select-border-radius-lg, var(--border-radius-lg), '--btn-input-lg-border-radius')}; } @if $enable-dark-mode { @include color-mode(dark) { .form-select { - --form-select-bg-img: #{escape-svg($form-select-indicator-dark)}; + --control-select-bg: var(--control-select-bg-dark); } } } diff --git a/core/scss/bootstrap/forms/_input-group.scss b/core/scss/bootstrap/forms/_input-group.scss index 9b2577c43..a16fcb6aa 100644 --- a/core/scss/bootstrap/forms/_input-group.scss +++ b/core/scss/bootstrap/forms/_input-group.scss @@ -7,7 +7,21 @@ @use '../../config' as *; +// Addon tokens, in the names Bootstrap 6 uses. The addon sits next to the +// control, not inside it, so it reads the `:root` scale rather than the +// control's own `--control-*` tokens. .input-group { + // scss-docs-start input-group-css-vars + --input-group-addon-padding-y: #{var-if-default($input-group-addon-padding-y, $input-btn-padding-y, '--btn-input-padding-y')}; + --input-group-addon-padding-x: #{var-if-default($input-group-addon-padding-x, $input-btn-padding-x, '--btn-input-padding-x')}; + @include rfs(var-if-default($input-font-size, $input-btn-font-size, '--btn-input-font-size'), --input-group-addon-font-size); + --input-group-addon-line-height: #{var-if-default($input-line-height, $input-btn-line-height, '--btn-input-line-height')}; + --input-group-addon-color: #{$input-group-addon-color}; + --input-group-addon-bg: #{$input-group-addon-bg}; + --input-group-addon-border-color: #{$input-group-addon-border-color}; + --input-group-addon-border-radius: #{var-if-default($input-border-radius, var(--border-radius), '--btn-input-border-radius')}; + // scss-docs-end input-group-css-vars + position: relative; display: flex; flex-wrap: wrap; // For form validation feedback @@ -51,48 +65,88 @@ .input-group-text { display: flex; align-items: center; - padding: $input-group-addon-padding-y $input-group-addon-padding-x; - @include font-size($input-font-size); // Match inputs + padding: var(--input-group-addon-padding-y) var(--input-group-addon-padding-x); + @include font-size(var(--input-group-addon-font-size)); // Match inputs font-weight: $input-group-addon-font-weight; - line-height: $input-line-height; - color: $input-group-addon-color; + line-height: var(--input-group-addon-line-height); + color: var(--input-group-addon-color); text-align: center; white-space: nowrap; - background-color: $input-group-addon-bg; - border: $input-border-width solid $input-group-addon-border-color; - @include border-radius($input-border-radius); + background-color: var(--input-group-addon-bg); + border: $input-border-width solid var(--input-group-addon-border-color); + @include border-radius(var(--input-group-addon-border-radius)); } // Sizing // -// Remix the default form control sizing classes into new ones for easier -// manipulation. +// A size moves the addon and control tokens; the button keeps its own rules +// until `.btn` reads the scale too. -.input-group-lg > .form-control, -.input-group-lg > .form-select, -.input-group-lg > .input-group-text, -.input-group-lg > .btn { - line-height: $input-line-height-lg; - min-height: $input-height-lg; - padding: $input-padding-y-lg $input-padding-x-lg; - @include font-size($input-font-size-lg); - @include border-radius($input-border-radius-lg); +@mixin -input-group-size($size) { + --input-group-addon-padding-y: #{var-if-default(map.get($-input-group-sizes, $size, 'padding-y'), map.get($-input-group-sizes, $size, 'scale-padding-y'), '--btn-input-#{$size}-padding-y')}; + --input-group-addon-padding-x: #{var-if-default(map.get($-input-group-sizes, $size, 'padding-x'), map.get($-input-group-sizes, $size, 'scale-padding-x'), '--btn-input-#{$size}-padding-x')}; + @include rfs(var-if-default(map.get($-input-group-sizes, $size, 'font-size'), map.get($-input-group-sizes, $size, 'scale-font-size'), '--btn-input-#{$size}-font-size'), --input-group-addon-font-size); + --input-group-addon-line-height: #{var-if-default(map.get($-input-group-sizes, $size, 'line-height'), map.get($-input-group-sizes, $size, 'scale-line-height'), '--btn-input-#{$size}-line-height')}; + --input-group-addon-border-radius: #{var-if-default(map.get($-input-group-sizes, $size, 'border-radius'), map.get($-input-group-sizes, $size, 'scale-border-radius'), '--btn-input-#{$size}-border-radius')}; + + > .form-control, + > .form-select { + --control-padding-y: var(--input-group-addon-padding-y); + --control-padding-x: var(--input-group-addon-padding-x); + --control-font-size: var(--input-group-addon-font-size); + --control-line-height: var(--input-group-addon-line-height); + --control-border-radius: var(--input-group-addon-border-radius); + min-height: var(--control-min-height); + } + + > .form-select { + padding-inline-end: calc(#{var-if-default($form-select-padding-x, $input-btn-padding-x, '--btn-input-padding-x')} + var(--control-select-indicator-padding)); + } + + > .btn { + line-height: map.get($-input-group-sizes, $size, 'line-height'); + min-height: map.get($-input-group-sizes, $size, 'height'); + padding: map.get($-input-group-sizes, $size, 'padding-y') map.get($-input-group-sizes, $size, 'padding-x'); + @include font-size(map.get($-input-group-sizes, $size, 'font-size')); + @include border-radius(map.get($-input-group-sizes, $size, 'border-radius')); + } } -.input-group-sm > .form-control, -.input-group-sm > .form-select, -.input-group-sm > .input-group-text, -.input-group-sm > .btn { - line-height: $input-line-height-sm; - min-height: $input-height-sm; - padding: $input-padding-y-sm $input-padding-x-sm; - @include font-size($input-font-size-sm); - @include border-radius($input-border-radius-sm); +$-input-group-sizes: ( + 'sm': ( + 'padding-y': $input-padding-y-sm, + 'padding-x': $input-padding-x-sm, + 'font-size': $input-font-size-sm, + 'line-height': $input-line-height-sm, + 'border-radius': $input-border-radius-sm, + 'height': $input-height-sm, + 'scale-padding-y': $input-btn-padding-y-sm, + 'scale-padding-x': $input-btn-padding-x-sm, + 'scale-font-size': $input-btn-font-size-sm, + 'scale-line-height': $input-btn-line-height-sm, + 'scale-border-radius': var(--border-radius-sm), + ), + 'lg': ( + 'padding-y': $input-padding-y-lg, + 'padding-x': $input-padding-x-lg, + 'font-size': $input-font-size-lg, + 'line-height': $input-line-height-lg, + 'border-radius': $input-border-radius-lg, + 'height': $input-height-lg, + 'scale-padding-y': $input-btn-padding-y-lg, + 'scale-padding-x': $input-btn-padding-x-lg, + 'scale-font-size': $input-btn-font-size-lg, + 'scale-line-height': $input-btn-line-height-lg, + 'scale-border-radius': var(--border-radius-lg), + ), +); + +.input-group-lg { + @include -input-group-size('lg'); } -.input-group-lg > .form-select, -.input-group-sm > .form-select { - padding-inline-end: $form-select-padding-x + $form-select-indicator-padding; +.input-group-sm { + @include -input-group-size('sm'); } // Rounded corners diff --git a/core/scss/bootstrap/forms/_labels.scss b/core/scss/bootstrap/forms/_labels.scss index 763fa7d20..5b49a38bb 100644 --- a/core/scss/bootstrap/forms/_labels.scss +++ b/core/scss/bootstrap/forms/_labels.scss @@ -15,24 +15,24 @@ // For use with horizontal and inline forms, when you need the label (or legend) // text to align with the form controls. .col-form-label { - padding-top: add($input-padding-y, $input-border-width); - padding-bottom: add($input-padding-y, $input-border-width); + padding-top: calc(#{var-if-default($input-padding-y, $input-btn-padding-y, '--btn-input-padding-y')} + #{$input-border-width}); + padding-bottom: calc(#{var-if-default($input-padding-y, $input-btn-padding-y, '--btn-input-padding-y')} + #{$input-border-width}); margin-bottom: 0; // Override the `` default @include font-size(inherit); // Override the `` default font-style: $form-label-font-style; font-weight: $form-label-font-weight; - line-height: $input-line-height; + line-height: #{var-if-default($input-line-height, $input-btn-line-height, '--btn-input-line-height')}; color: $form-label-color; } .col-form-label-lg { - padding-top: add($input-padding-y-lg, $input-border-width); - padding-bottom: add($input-padding-y-lg, $input-border-width); - @include font-size($input-font-size-lg); + padding-top: calc(#{var-if-default($input-padding-y-lg, $input-btn-padding-y-lg, '--btn-input-lg-padding-y')} + #{$input-border-width}); + padding-bottom: calc(#{var-if-default($input-padding-y-lg, $input-btn-padding-y-lg, '--btn-input-lg-padding-y')} + #{$input-border-width}); + @include font-size(var-if-default($input-font-size-lg, $input-btn-font-size-lg, '--btn-input-lg-font-size')); } .col-form-label-sm { - padding-top: add($input-padding-y-sm, $input-border-width); - padding-bottom: add($input-padding-y-sm, $input-border-width); - @include font-size($input-font-size-sm); + padding-top: calc(#{var-if-default($input-padding-y-sm, $input-btn-padding-y-sm, '--btn-input-sm-padding-y')} + #{$input-border-width}); + padding-bottom: calc(#{var-if-default($input-padding-y-sm, $input-btn-padding-y-sm, '--btn-input-sm-padding-y')} + #{$input-border-width}); + @include font-size(var-if-default($input-font-size-sm, $input-btn-font-size-sm, '--btn-input-sm-font-size')); } diff --git a/core/scss/mixins/_mixins.scss b/core/scss/mixins/_mixins.scss index 36b113dde..ff47a32ce 100644 --- a/core/scss/mixins/_mixins.scss +++ b/core/scss/mixins/_mixins.scss @@ -117,3 +117,20 @@ @content; } } + +// `$input-height` and its sizes as a token: the `:root` scale's min height +// (`--btn-input-min-height`) while the input metrics keep their defaults, the +// configured Sass value otherwise. Elements that sit next to a control (an icon +// addon, a select-group tile, an otp slot) match its height through it. +@function input-height-token($size: null) { + @if $size == 'sm' { + $default: add($input-btn-line-height-sm, add(calc($input-btn-padding-y-sm * 2), calc(#{$input-btn-border-width} * 2), false)); + @return if(sass($input-height-sm == $default): var(--btn-input-sm-min-height); else: $input-height-sm); + } + @if $size == 'lg' { + $default: add($input-btn-line-height-lg, add(calc($input-btn-padding-y-lg * 2), calc(#{$input-btn-border-width} * 2), false)); + @return if(sass($input-height-lg == $default): var(--btn-input-lg-min-height); else: $input-height-lg); + } + $default: add($input-btn-line-height, add(calc($input-btn-padding-y * 2), calc(#{$input-btn-border-width} * 2), false)); + @return if(sass($input-height == $default): var(--btn-input-min-height); else: $input-height); +} diff --git a/core/scss/tests/__snapshots__/css-var-prefix.test.mjs.snap b/core/scss/tests/__snapshots__/css-var-prefix.test.mjs.snap index 806dc266c..cc24d90a3 100644 --- a/core/scss/tests/__snapshots__/css-var-prefix.test.mjs.snap +++ b/core/scss/tests/__snapshots__/css-var-prefix.test.mjs.snap @@ -56,6 +56,11 @@ exports[`css custom-property prefixing > leaves vendor-owned names alone and pre "--tblr-chart-3", "--tblr-chart-4", "--tblr-chart-5", + "--tblr-control-font-size", + "--tblr-control-line-height", + "--tblr-control-padding-x", + "--tblr-control-padding-y", + "--tblr-control-placeholder-color", "--tblr-danger", "--tblr-focus-ring", "--tblr-focus-ring-color", diff --git a/core/scss/tests/_input-height-token.test.scss b/core/scss/tests/_input-height-token.test.scss new file mode 100644 index 000000000..cbabdc5ac --- /dev/null +++ b/core/scss/tests/_input-height-token.test.scss @@ -0,0 +1,14 @@ +// Tests for `input-height-token()` in scss/mixins/_mixins.scss: the height of +// a control as a token that reads the `:root` scale while the input metrics +// keep their defaults. + +@use 'sass:meta'; +@use '../config' as *; + +@include describe('input-height-token') { + @include it('reads the root scale for the default metrics') { + @include assert-equal(meta.inspect(input-height-token()), 'var(--btn-input-min-height)'); + @include assert-equal(meta.inspect(input-height-token('sm')), 'var(--btn-input-sm-min-height)'); + @include assert-equal(meta.inspect(input-height-token('lg')), 'var(--btn-input-lg-min-height)'); + } +} diff --git a/core/scss/tests/_var-if-default.test.scss b/core/scss/tests/_var-if-default.test.scss index c7e3fb4eb..04d9c00e4 100644 --- a/core/scss/tests/_var-if-default.test.scss +++ b/core/scss/tests/_var-if-default.test.scss @@ -13,6 +13,10 @@ @include assert-equal(var-if-default(1rem, 0.5625rem, --btn-input-padding-y), 1rem); } + @include it('takes the token name as a string too') { + @include assert-equal(meta.inspect(var-if-default(1rem, 1rem, '--btn-input-padding-x')), 'var(--btn-input-padding-x)'); + } + @include it('compares var() defaults by value') { @include assert-equal(meta.inspect(var-if-default(var(--border-radius), var(--border-radius), --btn-input-border-radius)), 'var(--btn-input-border-radius)'); @include assert-equal(meta.inspect(var-if-default(0, var(--border-radius), --btn-input-border-radius)), '0'); diff --git a/core/scss/ui/_forms.scss b/core/scss/ui/_forms.scss index c53b8ae53..5d5e9ef00 100644 --- a/core/scss/ui/_forms.scss +++ b/core/scss/ui/_forms.scss @@ -53,7 +53,7 @@ Form control &:disabled, &.disabled { - color: $form-secondary-color; + color: var(--control-disabled-color); user-select: none; } @@ -155,7 +155,7 @@ Input group */ .input-group { box-shadow: $input-box-shadow; - @include border-radius($input-border-radius); + @include border-radius(var(--input-group-addon-border-radius)); .form-control, .form-select, @@ -173,7 +173,7 @@ Input group &:focus-within { --focus-ring-offset: -1px; @include focus-ring(true); - @include border-radius($input-border-radius); + @include border-radius(var(--input-group-addon-border-radius)); .form-control, .input-group-text { @@ -183,7 +183,7 @@ Input group .form-control { &:focus { - border-color: $input-border-color; + border-color: var(--control-border-color); outline: 0; box-shadow: none; } @@ -199,7 +199,7 @@ Input group .input-group-text { z-index: 10; - background: $form-check-input-bg; + background: #{var-if-default($form-check-input-bg, var(--bg-forms), '--btn-input-bg')}; @include transition($input-transition); &:first-child { diff --git a/core/scss/ui/forms/_form-check.scss b/core/scss/ui/forms/_form-check.scss index 216df88a9..0d06f7686 100644 --- a/core/scss/ui/forms/_form-check.scss +++ b/core/scss/ui/forms/_form-check.scss @@ -24,11 +24,9 @@ Form check } .form-check-input { - --form-check-input-width: #{$form-check-input-width}; - margin-top: ($form-check-min-height - $form-check-input-width) * 0.5; - + margin-top: calc((#{$form-check-min-height} - var(--form-check-input-width)) / 2); background-size: var(--form-check-input-width); - box-shadow: $form-check-input-box-shadow; + box-shadow: var(--form-check-box-shadow); } .form-check-label { @@ -67,15 +65,15 @@ Form switch */ .form-switch { .form-check-input { - height: $form-switch-height; - margin-top: ($form-check-min-height - $form-switch-height) * 0.5; + height: var(--form-switch-height); + margin-top: calc((#{$form-check-min-height} - var(--form-switch-height)) / 2); @include transition(background-color $transition-time, background-position $transition-time, $focus-ring-transition); // An off switch that is disabled fills its track like a disabled input, // so it no longer looks like an off switch that can still be toggled. &:disabled:not(:checked) { - --form-switch-color: #{$form-switch-disabled-color}; - background-color: $form-switch-disabled-bg; + --form-switch-color: var(--form-switch-disabled-color); + background-color: var(--form-switch-disabled-bg); } } } diff --git a/core/scss/ui/forms/_form-icon.scss b/core/scss/ui/forms/_form-icon.scss index 0da902cfe..e23878096 100644 --- a/core/scss/ui/forms/_form-icon.scss +++ b/core/scss/ui/forms/_form-icon.scss @@ -30,7 +30,7 @@ Icon input align-items: center; justify-content: center; min-width: var(--input-icon-addon-width); - height: $input-height; + height: input-height-token(); font-size: 1.2em; color: var(--icon-color); pointer-events: none; diff --git a/core/scss/ui/forms/_form-otp.scss b/core/scss/ui/forms/_form-otp.scss index d82bfe2ed..36fa792a7 100644 --- a/core/scss/ui/forms/_form-otp.scss +++ b/core/scss/ui/forms/_form-otp.scss @@ -114,7 +114,7 @@ .otp-input:disabled ~ .otp-slots .otp-slot { color: var(--disabled-color); - background-color: $input-disabled-bg; + background-color: #{var-if-default($input-disabled-bg, var(--bg-forms-disabled), '--control-disabled-bg')}; } // Zero gap with shared, merged borders; only the first and last slot keep a @@ -149,12 +149,12 @@ } .otp-sm { - --otp-size: #{$input-height-sm}; + --otp-size: #{input-height-token('sm')}; --otp-font-size: #{$input-font-size-sm}; } .otp-lg { - --otp-size: #{$input-height-lg}; + --otp-size: #{input-height-token('lg')}; --otp-font-size: #{$input-font-size-lg}; } diff --git a/core/scss/ui/forms/_form-selectgroup.scss b/core/scss/ui/forms/_form-selectgroup.scss index 2e8bcdc4a..9a7024ef9 100644 --- a/core/scss/ui/forms/_form-selectgroup.scss +++ b/core/scss/ui/forms/_form-selectgroup.scss @@ -36,11 +36,11 @@ Select group .form-selectgroup-label { position: relative; display: block; - min-width: $input-height; - padding: $input-btn-padding-y $input-btn-padding-x; + min-width: input-height-token(); + padding: var(--btn-input-padding-y) var(--btn-input-padding-x); margin: 0; - font-size: $input-btn-font-size; - line-height: $input-line-height; + font-size: var(--btn-input-font-size); + line-height: #{var-if-default($input-line-height, $input-btn-line-height, '--btn-input-line-height')}; color: var(--secondary); text-align: center; cursor: pointer; @@ -95,8 +95,8 @@ Select group .form-selectgroup-check-floated { position: absolute; - inset-inline-end: $input-btn-padding-y; - top: $input-btn-padding-y; + inset-inline-end: var(--btn-input-padding-y); + top: var(--btn-input-padding-y); } .form-selectgroup-input:checked + .form-selectgroup-label { diff --git a/core/scss/ui/forms/_validation.scss b/core/scss/ui/forms/_validation.scss index 3e8edb4af..d16aeeb39 100644 --- a/core/scss/ui/forms/_validation.scss +++ b/core/scss/ui/forms/_validation.scss @@ -11,7 +11,7 @@ // `.btn`/`.btn-sm`/`.btn-lg` use for their own icons (see ui/_buttons.scss). %validation-icon-size { --form-feedback-icon-size: #{$input-btn-icon-size}; - --form-feedback-icon-inset: #{$input-padding-x}; + --form-feedback-icon-inset: #{var-if-default($input-padding-x, $input-btn-padding-x, '--btn-input-padding-x')}; padding-inline-end: calc(var(--form-feedback-icon-size) + var(--form-feedback-icon-inset) * 2) !important; background-position: right var(--form-feedback-icon-inset) center !important; background-size: var(--form-feedback-icon-size) var(--form-feedback-icon-size) !important; @@ -19,12 +19,12 @@ %validation-icon-size-sm { --form-feedback-icon-size: #{$input-btn-icon-size-sm}; - --form-feedback-icon-inset: #{$input-padding-x-sm}; + --form-feedback-icon-inset: #{var-if-default($input-padding-x-sm, $input-btn-padding-x-sm, '--btn-input-sm-padding-x')}; } %validation-icon-size-lg { --form-feedback-icon-size: #{$input-btn-icon-size-lg}; - --form-feedback-icon-inset: #{$input-padding-x-lg}; + --form-feedback-icon-inset: #{var-if-default($input-padding-x-lg, $input-btn-padding-x-lg, '--btn-input-lg-padding-x')}; } %validation-icon-size-textarea { diff --git a/core/scss/vendor/_tom-select.scss b/core/scss/vendor/_tom-select.scss index 82a61ec63..b88357da0 100644 --- a/core/scss/vendor/_tom-select.scss +++ b/core/scss/vendor/_tom-select.scss @@ -72,49 +72,25 @@ // The bootstrap5 preset hardcodes Bootstrap's own input metrics on // `.ts-control` (0.375rem 0.75rem padding, 1.5 line-height), so a Tom Select -// came out 5px shorter than a `.form-control` next to it (#2567). Use Tabler's -// input tokens instead, in every size. The multi-value control follows the -// preset's formula: take the item's 1px padding, 1px border and 3px bottom -// margin out of the control's padding so the chips fill the same height. +// came out 5px shorter than a `.form-control` next to it (#2567). The wrapper +// carries the `.form-control` / `.form-select` class, so it holds the +// `--control-*` tokens: read them and every size follows on its own. The +// multi-value control follows the preset's formula: take the item's 1px +// padding, 1px border and 3px bottom margin out of the control's padding so +// the chips fill the same height. The font size is `!important` because the +// preset sets its own on `.form-select-sm.has-items .ts-control`, which +// outranks this selector. .ts-wrapper.form-control, .ts-wrapper.form-select { .ts-control { - padding: $input-padding-y $input-padding-x; - font-size: $input-font-size; - line-height: $input-line-height; + padding: var(--control-padding-y) var(--control-padding-x); + font-size: var(--control-font-size) !important; + line-height: var(--control-line-height); } &.multi.has-items .ts-control { - padding-top: calc(#{$input-padding-y} - 2px) !important; - padding-bottom: calc(#{$input-padding-y} - 5px); - } - - &.form-control-sm, - &.form-select-sm { - .ts-control { - padding: $input-padding-y-sm $input-padding-x-sm; - font-size: $input-font-size-sm; - line-height: $input-line-height-sm; - } - - &.multi.has-items .ts-control { - padding-top: calc(#{$input-padding-y-sm} - 2px) !important; - padding-bottom: calc(#{$input-padding-y-sm} - 5px); - } - } - - &.form-control-lg, - &.form-select-lg { - .ts-control { - padding: $input-padding-y-lg $input-padding-x-lg; - font-size: $input-font-size-lg; - line-height: $input-line-height-lg; - } - - &.multi.has-items .ts-control { - padding-top: calc(#{$input-padding-y-lg} - 2px) !important; - padding-bottom: calc(#{$input-padding-y-lg} - 5px); - } + padding-top: calc(var(--control-padding-y) - 2px) !important; + padding-bottom: calc(var(--control-padding-y) - 5px); } } @@ -172,7 +148,7 @@ .ts-control input { &::placeholder { - color: $input-placeholder-color; + color: var(--control-placeholder-color, #{$input-placeholder-color}); } } diff --git a/docs/content/ui/forms/css-variables.mdx b/docs/content/ui/forms/css-variables.mdx new file mode 100644 index 000000000..9e439ea72 --- /dev/null +++ b/docs/content/ui/forms/css-variables.mdx @@ -0,0 +1,259 @@ +--- +title: CSS variables +seoTitle: Form CSS variables +order: 11 +summary: Every form control reads its size, colors and states from CSS variables, so you can restyle inputs, selects, checks and switches at runtime without recompiling Sass. +description: Reference of the CSS variables that size and color Tabler form controls, with examples of runtime overrides. +related: [/ui/forms/elements, /ui/forms/floating-labels, /ui/getting-started/customize] +--- +import Example from '@components/Example.astro'; + +## Overview + +Form controls are built on three layers of custom properties. Each layer reads the one above it, so you can change a single value or a single control. + +1. **The control scale on `:root`.** `--tblr-btn-input-*` holds the padding, font size, line height, radius and minimum height of a control, with a `-sm-*`, `-lg-*` and `-xl-*` set for each size. `--tblr-control-checked-bg` and its siblings hold the state colors that checks, radios and switches share. +2. **Component tokens on the control.** `.form-control` and `.form-select` carry `--tblr-control-*`, `.input-group` carries `--tblr-input-group-addon-*`, `.form-floating` carries `--tblr-form-floating-*`, and so on. By default each token points at the scale. +3. **The declarations.** Every `padding`, `color` or `border` in the stylesheet reads a token, never a fixed value. + +The names follow Bootstrap 6, so a project that moves to a future major keeps its overrides. + +Sass still works. A `$input-*`, `$form-select-*` or `$form-check-*` variable that you set in Sass is compiled into the token as a fixed value. That control then ignores the scale on `:root`, as it did before the tokens existed. + +## Usage + +### Change every control at once + +Override the scale on `:root`. Inputs, selects, add-ons, floating labels, select-group tiles and column labels all follow. + +```css +:root { + --tblr-btn-input-padding-y: 0.75rem; + --tblr-btn-input-padding-x: 1.25rem; + --tblr-btn-input-border-radius: 0; +} +``` + +The scale inherits, so the same variables on a container change only the controls inside it. + + +
+
+ + +
+
+ @ + +
+
+
+ +### Change one control + +Set a component token on the element. Tokens on the element win over the scale. + + + + + + +### Make a custom size + +The size classes only move the size tokens, so a custom size is a few tokens on a class of your own. + +```css +.form-control-xs { + --tblr-control-padding-y: 0.125rem; + --tblr-control-padding-x: 0.5rem; + --tblr-control-font-size: 0.75rem; + --tblr-control-line-height: 1rem; +} +``` + +The minimum height, the file selector button and the color swatch follow the tokens on their own. + +### Recolor checks, radios and switches + +The checked and disabled colors of every check control come from `:root`. + + +
+ + + +
+
+ +## Reference + +### Control scale on `:root` + +| Variable | Default | +| --- | --- | +| `--tblr-btn-input-fg` | `var(--tblr-body-color)` | +| `--tblr-btn-input-bg` | `var(--tblr-bg-forms)` | +| `--tblr-btn-input-padding-y` | `0.5625rem` | +| `--tblr-btn-input-padding-x` | `1rem` | +| `--tblr-btn-input-font-size` | `0.875rem` | +| `--tblr-btn-input-line-height` | `1.25rem` | +| `--tblr-btn-input-border-radius` | `var(--tblr-border-radius)` | +| `--tblr-btn-input-min-height` | line height + 2 × padding + 2 × border | + +The same set exists per size as `--tblr-btn-input-sm-*`, `--tblr-btn-input-lg-*` and `--tblr-btn-input-xl-*`. + +| Size | Padding | Font size | Line height | Radius | +| --- | --- | --- | --- | --- | +| `sm` | `0.4375rem 0.5rem` | `0.75rem` | `1rem` | `var(--tblr-border-radius-sm)` | +| `lg` | `0.6875rem 1.5rem` | `1rem` | `1.5rem` | `var(--tblr-border-radius-lg)` | +| `xl` | `0.6875rem 1.75rem` | `1.5rem` | `2rem` | `var(--tblr-border-radius)` | + +### State colors on `:root` + +| Variable | Default | Read by | +| --- | --- | --- | +| `--tblr-control-checked-bg` | `var(--tblr-primary)` | checked and indeterminate checks, radios, switches | +| `--tblr-control-checked-border-color` | `var(--tblr-border-color-translucent)` | checked checks and radios | +| `--tblr-control-disabled-bg` | `var(--tblr-bg-forms-disabled)` | disabled switches and OTP slots | +| `--tblr-control-disabled-opacity` | `0.5` | disabled checks, radios, switches | + +### Inputs and selects + +Set on `.form-control`, `.form-control-plaintext` and `.form-select`. `.form-control-sm/-lg` and `.form-select-sm/-lg` override the first five with the matching size of the scale. + +| Variable | Default | +| --- | --- | +| `--tblr-control-padding-y` | `var(--tblr-btn-input-padding-y)` | +| `--tblr-control-padding-x` | `var(--tblr-btn-input-padding-x)` | +| `--tblr-control-font-size` | `var(--tblr-btn-input-font-size)` | +| `--tblr-control-line-height` | `var(--tblr-btn-input-line-height)` | +| `--tblr-control-border-radius` | `var(--tblr-btn-input-border-radius)` | +| `--tblr-control-min-height` | line height + 2 × padding + 2 × border | +| `--tblr-control-fg` | `var(--tblr-btn-input-fg)` | +| `--tblr-control-bg` | `var(--tblr-btn-input-bg)` | +| `--tblr-control-border-width` | `var(--tblr-border-width)` | +| `--tblr-control-border-color` | `var(--tblr-border-color)` | +| `--tblr-control-box-shadow` | `var(--tblr-shadow-input)` | +| `--tblr-control-focus-fg` | `var(--tblr-control-fg)` | +| `--tblr-control-focus-bg` | `var(--tblr-control-bg)` | +| `--tblr-control-focus-border-color` | primary mixed with white | +| `--tblr-control-placeholder-color` | `var(--tblr-tertiary)` | +| `--tblr-control-disabled-color` | `var(--tblr-secondary)` | +| `--tblr-control-disabled-bg` | `var(--tblr-bg-forms-disabled)` | +| `--tblr-control-disabled-border-color` | `var(--tblr-control-border-color)` | +| `--tblr-control-action-bg` | `var(--tblr-tertiary-bg)` (file selector button) | +| `--tblr-control-action-hover-bg` | `var(--tblr-secondary-bg)` | +| `--tblr-control-transition` | border color, outline color and shadow, `0.15s` | + +`.form-select` adds the arrow: `--tblr-control-select-bg`, `--tblr-control-select-bg-dark`, `--tblr-control-select-bg-position`, `--tblr-control-select-bg-size` and `--tblr-control-select-indicator-padding` (the room kept for the arrow, `3 ×` the scale's horizontal padding). + +Tom Select copies the `.form-control` or `.form-select` class onto its wrapper, so its control reads the same tokens. + +### Input groups + +Set on `.input-group`. `.input-group-sm/-lg` override the size tokens and pass them to the controls in the group. + +| Variable | Default | +| --- | --- | +| `--tblr-input-group-addon-padding-y` | `var(--tblr-btn-input-padding-y)` | +| `--tblr-input-group-addon-padding-x` | `var(--tblr-btn-input-padding-x)` | +| `--tblr-input-group-addon-font-size` | `var(--tblr-btn-input-font-size)` | +| `--tblr-input-group-addon-line-height` | `var(--tblr-btn-input-line-height)` | +| `--tblr-input-group-addon-color` | `var(--tblr-gray-500)` | +| `--tblr-input-group-addon-bg` | `var(--tblr-bg-surface-secondary)` | +| `--tblr-input-group-addon-border-color` | `var(--tblr-border-color)` | +| `--tblr-input-group-addon-border-radius` | `var(--tblr-btn-input-border-radius)` | + +### Floating labels + +Set on `.form-floating`. + +| Variable | Default | +| --- | --- | +| `--tblr-form-floating-height` | `3.5rem` + 2 × border | +| `--tblr-form-floating-line-height` | `1.25` | +| `--tblr-form-floating-padding-x` | `var(--tblr-btn-input-padding-x)` | +| `--tblr-form-floating-padding-y` | `1rem` | +| `--tblr-form-floating-input-padding-t` | `1.625rem` | +| `--tblr-form-floating-input-padding-b` | `0.625rem` | +| `--tblr-form-floating-label-height` | `1.5em` | +| `--tblr-form-floating-label-bg` | `var(--tblr-btn-input-bg)` (masks the text under a textarea label) | +| `--tblr-form-floating-label-disabled-bg` | `var(--tblr-bg-forms-disabled)` | +| `--tblr-form-floating-label-border-radius` | `var(--tblr-btn-input-border-radius)` | +| `--tblr-form-floating-label-opacity` | `0.65` | +| `--tblr-form-floating-label-transform` | `scale(0.85) translateY(-0.5rem) translateX(0.15rem)` | +| `--tblr-form-floating-label-disabled-color` | gray `600` in light mode, `400` in dark | +| `--tblr-form-floating-transition` | opacity and transform, `0.1s` | + +### Range inputs + +Set on `.form-range`. + +| Variable | Default | +| --- | --- | +| `--tblr-range-track-width` | `100%` | +| `--tblr-range-track-height` | `0.25rem` | +| `--tblr-range-track-cursor` | `pointer` | +| `--tblr-range-track-bg` | `var(--tblr-border-color)` | +| `--tblr-range-track-border-radius` | `1rem` | +| `--tblr-range-track-box-shadow` | `var(--tblr-box-shadow-inset)` | +| `--tblr-range-thumb-width` | `1rem` | +| `--tblr-range-thumb-height` | `1rem` | +| `--tblr-range-thumb-bg` | `var(--tblr-primary)` | +| `--tblr-range-thumb-border` | `2px` white | +| `--tblr-range-thumb-border-radius` | `1rem` | +| `--tblr-range-thumb-box-shadow` | soft black shadow | +| `--tblr-range-thumb-active-bg` | primary mixed with white | +| `--tblr-range-thumb-disabled-bg` | `var(--tblr-secondary-color)` | +| `--tblr-range-thumb-transition` | background, border, outline and shadow, `0.15s` | + +### Checks and radios + +`.form-check` carries the layout: `--tblr-form-check-min-height` (`1.25rem`), `--tblr-form-check-padding-start` (`2rem`) and `--tblr-form-check-margin-bottom` (`0.75rem`). The input itself carries the look. + +| Variable on `.form-check-input` | Default | +| --- | --- | +| `--tblr-form-check-input-width` | `1.25rem` | +| `--tblr-form-check-bg` | `var(--tblr-bg-forms)` | +| `--tblr-form-check-border` | `var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)` | +| `--tblr-form-check-border-radius` | `var(--tblr-border-radius)` | +| `--tblr-form-check-box-shadow` | `var(--tblr-shadow-input)` | +| `--tblr-form-check-icon-color` | `#fff` | +| `--tblr-form-check-focus-border-color` | primary mixed with white | +| `--tblr-form-check-active-filter` | `brightness(90%)` | +| `--tblr-form-check-checked-bg` | `var(--tblr-control-checked-bg)` | +| `--tblr-form-check-checked-border-color` | `var(--tblr-control-checked-border-color)` | +| `--tblr-form-check-indeterminate-bg` | `var(--tblr-control-checked-bg)` | +| `--tblr-form-check-indeterminate-border-color` | `var(--tblr-form-check-indeterminate-bg)` | +| `--tblr-form-check-disabled-opacity` | `var(--tblr-control-disabled-opacity)` | +| `--tblr-form-check-transition` | outline color, `0.15s` | + +### Switches + +Set on `.form-switch`. The knob is three quarters of the height and slides by the width, so both follow these two tokens. + +| Variable | Default | +| --- | --- | +| `--tblr-form-switch-width` | `2rem` | +| `--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-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 | + +`.form-switch-lg` keeps its own `--tblr-form-switch-lg-*` set. + +## Sass variables + +The tokens are seeded from the Sass variables of the same component: `$input-*` for inputs, `$form-select-*` for selects, `$input-group-addon-*`, `$form-floating-*`, `$form-range-*`, `$form-check-*` and `$form-switch-*`. Configure them through `@use` as before. A configured variable becomes a fixed token value, so it no longer follows `--tblr-btn-input-*` at runtime; leave it at its default to keep the runtime link. + +```scss +@use '@tabler/core/scss/tabler' with ( + $input-padding-y: 0.75rem, + $input-border-radius: 0 +); +``` diff --git a/docs/content/ui/forms/elements.mdx b/docs/content/ui/forms/elements.mdx index cdc856cfb..e2da81bf2 100644 --- a/docs/content/ui/forms/elements.mdx +++ b/docs/content/ui/forms/elements.mdx @@ -6,6 +6,7 @@ docs-libs: [nouislider] description: Styled inputs, selects, checkboxes and radios, with states, sizes and layout options. order: 1 related: [/ui/forms/validation, /ui/forms/helpers, /ui/forms/fieldset, /ui/plugins/date-picker, /ui/plugins/advanced-select] +css-vars: control sass-vars: input --- import Example from '@components/Example.astro'; @@ -287,6 +288,8 @@ Use radio buttons for the parts of your form which require users to choose one o Use checkboxes if you want to allow users to select more than one option from a set of predefined options or to turn an option on or off. +Checks, radios and switches share their state colors through `--tblr-control-checked-bg`, `--tblr-control-checked-border-color`, `--tblr-control-disabled-bg` and `--tblr-control-disabled-opacity` on `:root`. Each control also has its own tokens: `--tblr-form-check-*` on `.form-check-input` and `--tblr-form-switch-*` on `.form-switch`. +
Checkboxes
@@ -328,6 +331,8 @@ Use checkboxes if you want to allow users to select more than one option from a Add a [range slider](/ui/plugins/range-slider) to make it possible for users to set a value or range, such as a price range or a time frame. +The track and the thumb read `--tblr-range-*` custom properties on `.form-range`: `--tblr-range-track-height`, `--tblr-range-track-bg`, `--tblr-range-thumb-width`, `--tblr-range-thumb-bg`, `--tblr-range-thumb-border` and the rest of the `track-*` and `thumb-*` set. +
@@ -336,6 +341,8 @@ Add a [range slider](/ui/plugins/range-slider) to make it possible for users to Create a group of input controls and place add-ons on either side of an input. +The add-on reads `--tblr-input-group-addon-*` custom properties on `.input-group` (`padding-y`, `padding-x`, `font-size`, `line-height`, `color`, `bg`, `border-color`, `border-radius`). `.input-group-sm` and `.input-group-lg` only change these tokens and pass them on to the controls in the group. +
diff --git a/docs/content/ui/forms/floating-labels.mdx b/docs/content/ui/forms/floating-labels.mdx index ab596e77f..6428aa28c 100644 --- a/docs/content/ui/forms/floating-labels.mdx +++ b/docs/content/ui/forms/floating-labels.mdx @@ -3,6 +3,7 @@ title: Floating labels order: 3 summary: Floating labels put the label inside the field. The label sits over the empty control and moves up when the user types or selects a value. description: Show labels inside form controls. +css-vars: form-floating --- import Example from '@components/Example.astro'; diff --git a/shared/data/docs.json b/shared/data/docs.json index ba3a7ac42..883a93d8b 100644 --- a/shared/data/docs.json +++ b/shared/data/docs.json @@ -383,6 +383,10 @@ { "title": "OTP input", "url": "/ui/forms/otp-input" + }, + { + "title": "CSS variables", + "url": "/ui/forms/css-variables" } ] },