89 lines
3.9 KiB
JavaScript
89 lines
3.9 KiB
JavaScript
// The "deep overlay": emit the per-component --bs-btn-* variables that Bootstrap
|
|
// 5.3 bakes at Sass compile time, recomputed from the theme's colors -- so solid
|
|
// and outline BUTTONS actually recolor under the CSS-variable overlay (a plain
|
|
// :root{--bs-primary} override does NOT recolor .btn-primary, whose colors are
|
|
// build-time literals). Mirrors scss/_buttons.scss + mixins/_buttons.scss.
|
|
|
|
const {
|
|
parseHexColor, toHex, toRgbString, mix, shade, tint, colorContrast,
|
|
} = require("./bootstrapColor");
|
|
|
|
// The themeable button variants, in Bootstrap order.
|
|
const BTN_VARIANTS = ["primary", "secondary", "success", "info", "warning", "danger", "light", "dark"];
|
|
|
|
// button-variant hover/active shade & tint amounts (scss/_variables.scss).
|
|
const A = {
|
|
hoverBgShade: 15, hoverBgTint: 15,
|
|
hoverBorderShade: 20, hoverBorderTint: 10,
|
|
activeBgShade: 20, activeBgTint: 20,
|
|
activeBorderShade: 25, activeBorderTint: 10,
|
|
};
|
|
|
|
|
|
// One solid .btn-<name> rule. `name` selects the light/dark force-overrides:
|
|
// .btn-light -> always SHADE; .btn-dark -> always TINT; else by text color.
|
|
function solidButtonRule(name, color) {
|
|
const c = parseHexColor(color);
|
|
if (!c) {
|
|
return "";
|
|
}
|
|
const textColor = colorContrast(c); // "#ffffff" | "#000000"
|
|
const shadeMode = name === "light" ? true : name === "dark" ? false : textColor === "#ffffff";
|
|
|
|
const hoverBg = shadeMode ? shade(c, A.hoverBgShade) : tint(c, A.hoverBgTint);
|
|
const hoverBorder = shadeMode ? shade(c, A.hoverBorderShade) : tint(c, A.hoverBorderTint);
|
|
const activeBg = shadeMode ? shade(c, A.activeBgShade) : tint(c, A.activeBgTint);
|
|
const activeBorder = shadeMode ? shade(c, A.activeBorderShade) : tint(c, A.activeBorderTint);
|
|
const focusRgb = toRgbString(mix(parseHexColor(textColor), c, 0.15));
|
|
|
|
const bg = toHex(c);
|
|
return `.btn-${name}{`
|
|
+ `--bs-btn-color:${textColor};--bs-btn-bg:${bg};--bs-btn-border-color:${bg};`
|
|
+ `--bs-btn-hover-color:${colorContrast(hoverBg)};--bs-btn-hover-bg:${toHex(hoverBg)};--bs-btn-hover-border-color:${toHex(hoverBorder)};`
|
|
+ `--bs-btn-focus-shadow-rgb:${focusRgb};`
|
|
+ `--bs-btn-active-color:${colorContrast(activeBg)};--bs-btn-active-bg:${toHex(activeBg)};--bs-btn-active-border-color:${toHex(activeBorder)};`
|
|
+ `--bs-btn-disabled-color:${textColor};--bs-btn-disabled-bg:${bg};--bs-btn-disabled-border-color:${bg};`
|
|
+ `}`;
|
|
}
|
|
|
|
|
|
// One .btn-outline-<name> rule (button-outline-variant): transparent fill that
|
|
// inverts to the variant color on hover/active.
|
|
function outlineButtonRule(name, color) {
|
|
const c = parseHexColor(color);
|
|
if (!c) {
|
|
return "";
|
|
}
|
|
const hex = toHex(c);
|
|
const onColor = colorContrast(c);
|
|
return `.btn-outline-${name}{`
|
|
+ `--bs-btn-color:${hex};--bs-btn-border-color:${hex};`
|
|
+ `--bs-btn-hover-color:${onColor};--bs-btn-hover-bg:${hex};--bs-btn-hover-border-color:${hex};`
|
|
+ `--bs-btn-focus-shadow-rgb:${toRgbString(c)};`
|
|
+ `--bs-btn-active-color:${onColor};--bs-btn-active-bg:${hex};--bs-btn-active-border-color:${hex};`
|
|
+ `--bs-btn-disabled-color:${hex};--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:${hex};`
|
|
+ `}`;
|
|
}
|
|
|
|
|
|
// Emit the full deep-overlay CSS for the given color map { primary, secondary,
|
|
// ... }. Variants whose value is not a hex color are skipped (the :root overlay
|
|
// still sets --bs-<name> for them).
|
|
function emitButtonRules(colors) {
|
|
if (!colors || typeof colors !== "object") {
|
|
return "";
|
|
}
|
|
const out = [];
|
|
for (const name of BTN_VARIANTS) {
|
|
const color = colors[name];
|
|
if (!color || !parseHexColor(color)) {
|
|
continue;
|
|
}
|
|
out.push(solidButtonRule(name, color));
|
|
out.push(outlineButtonRule(name, color));
|
|
}
|
|
return out.filter(Boolean).join("\n");
|
|
}
|
|
|
|
|
|
module.exports = { emitButtonRules, solidButtonRule, outlineButtonRule, BTN_VARIANTS };
|