@atlaskit/tokens
Version:
Design tokens are the single source of truth to name and store design decisions.
151 lines (139 loc) • 5.85 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.CUSTOM_THEME_SCOPE_ATTRIBUTE = void 0;
exports.getThemeAndOverrides = getThemeAndOverrides;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
/**
* Inputs for the dynamic colour themes. Every colour token is derived from these two colours.
*/
/**
* Typed inputs for the experimental semantic spacing theme.
*/
/**
* Optional unitless multipliers for individual heading sizes, applied on top of `dynamicFontScale`.
* Each defaults to `1`, e.g. `dynamicFontHeadingXxlargeScale: 1.5` makes only `xxlarge` headings
* larger.
*/
/**
* Optional line-height multipliers for individual heading sizes, applied on top of font, heading
* and global line-height scales without changing font sizes. Each defaults to `1`.
*/
/**
* The variable contract for custom themes that support runtime overrides.
*
* Add a theme here when it is introduced. Its theme dimensions and required override values are
* then shared by TypeScript consumers.
*/
/**
* A theme configuration that can provide typed overrides alongside a custom theme ID.
*
* @example
* ```tsx
* <AppProvider defaultTheme={{
* light: {
* id: 'UNSAFE-dynamic',
* overrides: { dynamicForeground: '#172b4d', dynamicBackground: '#ffffff' },
* },
* dark: {
* id: 'UNSAFE-dynamic-dark',
* overrides: { dynamicForeground: '#ffffff', dynamicBackground: '#1d2125' },
* },
* }}
* />
* ```
*/
/**
* Custom CSS variable overrides grouped by the theme dimension and theme ID they apply to.
*/
function isThemeWithOverrides(value) {
return (0, _typeof2.default)(value) === 'object' && value !== null && 'id' in value && 'overrides' in value && typeof value.id === 'string' && (0, _typeof2.default)(value.overrides) === 'object' && value.overrides !== null;
}
/**
* Marks a subtree theme element with its provider's scope, so its override styles only match that
* element.
*/
var CUSTOM_THEME_SCOPE_ATTRIBUTE = exports.CUSTOM_THEME_SCOPE_ATTRIBUTE = 'data-theme-overrides-scope';
/**
* Separates custom theme override values from the plain theme IDs consumed by the theme runtime.
*
* Pass a `scope` for subtree themes. The override selector then only matches the element carrying
* `CUSTOM_THEME_SCOPE_ATTRIBUTE` with that value, so sibling subtrees using the same custom theme
* with different values don't overwrite each other. Without a scope, the styles target `html` and
* any subtree using the theme.
*/
function getThemeAndOverrides(themeWithOverrides, scope) {
var theme = {};
var overrides = {};
for (var _i = 0, _Object$entries = Object.entries(themeWithOverrides || {}); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = (0, _slicedToArray2.default)(_Object$entries[_i], 2),
themeKind = _Object$entries$_i[0],
value = _Object$entries$_i[1];
if (isThemeWithOverrides(value)) {
theme[themeKind] = value.id;
overrides[themeKind] = (0, _defineProperty2.default)({}, value.id, value.overrides);
continue;
}
theme[themeKind] = value;
}
return {
theme: theme,
inlineStyles: getCustomThemeOverrideStyles(overrides, scope)
};
}
/**
* Escapes a value for use inside a double-quoted CSS attribute selector.
*/
function escapeAttributeValue(value) {
return value.replace(/["\\]/g, '\\$&');
}
function getThemeOverrideSelector(themeKind, themeName, scope) {
// Colour themes only apply in their own colour mode.
var colorMode = themeKind === 'light' || themeKind === 'dark' ? "[data-color-mode=\"".concat(themeKind, "\"]") : '';
var theme = "".concat(colorMode, "[data-theme~=\"").concat(themeKind, ":").concat(themeName, "\"]");
if (scope) {
return "[data-subtree-theme][".concat(CUSTOM_THEME_SCOPE_ATTRIBUTE, "=\"").concat(escapeAttributeValue(scope), "\"]").concat(theme);
}
return "html".concat(theme, ", [data-subtree-theme]").concat(theme);
}
function getCssVariableName(propertyName) {
return "--ds-".concat(propertyName.replace(/([A-Z])/g, '-$1').toLowerCase());
}
/**
* Generates CSS for custom theme overrides.
*
* Override property names are converted from camel case to `--ds-` CSS custom properties.
*/
function getCustomThemeOverrideStyles(overrides, scope) {
if (!overrides) {
return '';
}
return Object.entries(overrides).flatMap(function (_ref) {
var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
themeKind = _ref2[0],
themes = _ref2[1];
return Object.entries(themes || {}).flatMap(function (_ref3) {
var _ref4 = (0, _slicedToArray2.default)(_ref3, 2),
themeName = _ref4[0],
values = _ref4[1];
var styles = Object.entries(values || {})
// Unset optional inputs are skipped so the theme's `var()` fallback applies. Writing
// `undefined` would be a valid custom property value that breaks the formula.
.filter(function (_ref5) {
var _ref6 = (0, _slicedToArray2.default)(_ref5, 2),
value = _ref6[1];
return value !== undefined && value !== null;
}).map(function (_ref7) {
var _ref8 = (0, _slicedToArray2.default)(_ref7, 2),
propertyName = _ref8[0],
value = _ref8[1];
return "".concat(getCssVariableName(propertyName), ": ").concat(typeof value === 'boolean' ? Number(value) : value, ";");
}).join('');
return styles ? "\n\t\t".concat(getThemeOverrideSelector(themeKind, themeName, scope), " {\n\t\t\t").concat(styles, "\n\t\t}\n\t") : [];
});
}).join('');
}