unocss-preset-useful
Version:
My useful preset about unocss usage.
832 lines (748 loc) • 20.9 kB
JavaScript
import { definePreset, mergeConfigs, toArray as toArray$1 } from "@unocss/core";
import postcss from "postcss";
import postcssJs, { objectify } from "postcss-js";
//#region src/core/extractors.ts
const extractors = [{
name: "unocss-preset-useful-extractor-includes-base64",
order: 0,
extract({ code }) {
return [...new Set(code.split(/[\\:]?[\s'"`{}]|;(?!base64)+/g))];
}
}];
//#endregion
//#region src/core/postprocess/important.ts
function createFilter(include, exclude) {
const includePattern = toArray$1(include || []);
const excludePattern = toArray$1(exclude || []);
return (id) => {
if (excludePattern.some((p) => id.match(p))) return false;
return includePattern.some((p) => id.match(p));
};
}
function importantProcess(importantOptions) {
const keyFilter = createFilter(importantOptions.includes, importantOptions.excludes);
return (util) => {
for (const item of util.entries) if (keyFilter(item[0])) {
if (item[1] != null && !String(item[1]).includes("!important")) item[1] += " !important";
} else if (item[1] != null && String(item[1]).includes("!important")) item[1] = String(item[1]).replace(/\s*!important/g, "");
};
}
//#endregion
//#region src/core/postprocess/uncolor.ts
const rgbRE = /rgb\(([\d\s]+?)\s*\/\s*([^)]+)\)/;
const rgbaRE = /rgba\(([\d\s,]+),\s*([^)]+)\)/;
function postprocessWithUnColor(unColor) {
return (util) => {
util.entries.forEach((i) => {
const value = i[1];
if (typeof value === "string") {
let match = value.match(rgbaRE);
if (match != null) {
i[1] = value.replace(rgbaRE, `rgba(var(${unColor}) , $2)`);
util.entries.unshift([unColor, match[1]]);
} else {
match = value.match(rgbRE);
if (match != null) {
i[1] = value.replace(rgbRE, `rgb(var(${unColor}) / $2)`);
util.entries.unshift([unColor, match[1].trim()]);
}
}
}
});
};
}
//#endregion
//#region src/core/postprocess/index.ts
function postprocess(options) {
const { unColor, important } = options;
return [unColor ? postprocessWithUnColor(unColor) : void 0, important ? importantProcess(important) : void 0].filter(Boolean);
}
//#endregion
//#region src/meta.ts
const PRESET_NAME = "useful";
const layerMeta = { layer: PRESET_NAME };
//#endregion
//#region src/core/preflights/reset.ts
const resetCSS = `/*
Please read: https://github.com/unocss/unocss/blob/main/packages-presets/reset/tailwind-compat.md
*/
/*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
2. [UnoCSS]: allow to override the default border color with css var \`--un-default-border-color\`
*/
*,
::before,
::after {
box-sizing: border-box; /* 1 */
border-width: 0; /* 2 */
border-style: solid; /* 2 */
border-color: var(--un-default-border-color, #e5e7eb); /* 2 */
}
/*
1. Use a consistent sensible line-height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured \`sans\` font-family by default.
5. Use the user's configured \`sans\` font-feature-settings by default.
6. Use the user's configured \`sans\` font-variation-settings by default.
7. Disable tap highlights on iOS.
*/
html,
:host {
line-height: 1.5; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-moz-tab-size: 4; /* 3 */
tab-size: 4; /* 3 */
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */
font-feature-settings: normal; /* 5 */
font-variation-settings: normal; /* 6 */
-webkit-tap-highlight-color: transparent; /* 7 */
}
/*
1. Remove the margin in all browsers.
2. Inherit line-height from \`html\` so users can set them as a class directly on the \`html\` element.
*/
body {
margin: 0; /* 1 */
line-height: inherit; /* 2 */
}
/*
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
3. Ensure horizontal rules are visible by default.
*/
hr {
height: 0; /* 1 */
color: inherit; /* 2 */
border-top-width: 1px; /* 3 */
}
/*
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr:where([title]) {
text-decoration: underline dotted;
}
/*
Remove the default font size and weight for headings.
*/
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
/*
Reset links to optimize for opt-in styling instead of opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/*
Add the correct font weight in Edge and Safari.
*/
b,
strong {
font-weight: bolder;
}
/*
1. Use the user's configured \`mono\` font-family by default.
2. Use the user's configured \`mono\` font-feature-settings by default.
3. Use the user's configured \`mono\` font-variation-settings by default.
4. Correct the odd \`em\` font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */
font-feature-settings: normal; /* 2 */
font-variation-settings: normal; /* 3 */
font-size: 1em; /* 4 */
}
/*
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/*
Prevent \`sub\` and \`sup\` elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/*
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
3. Remove gaps between table borders by default.
*/
table {
text-indent: 0; /* 1 */
border-color: inherit; /* 2 */
border-collapse: collapse; /* 3 */
}
/*
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
3. Remove default padding in all browsers.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-feature-settings: inherit; /* 1 */
font-variation-settings: inherit; /* 1 */
font-size: 100%; /* 1 */
font-weight: inherit; /* 1 */
line-height: inherit; /* 1 */
color: inherit; /* 1 */
margin: 0; /* 2 */
padding: 0; /* 3 */
}
/*
Remove the inheritance of text transform in Edge and Firefox.
*/
button,
select {
text-transform: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button; /* 1 */
/* Will affect the button style of most component libraries, so disable it */
/* https://github.com/unocss/unocss/issues/2127 */
/* background-color: transparent; !* 2 *! */
background-image: none; /* 2 */
}
/*
Use the modern Firefox focus style for all focusable elements.
*/
:-moz-focusring {
outline: auto;
}
/*
Remove the additional \`:invalid\` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/
:-moz-ui-invalid {
box-shadow: none;
}
/*
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/*
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/*
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/*
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to \`inherit\` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
/*
Removes the default spacing for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
fieldset {
margin: 0;
padding: 0;
}
legend {
padding: 0;
}
ol,
ul,
menu {
list-style: none;
margin: 0;
padding: 0;
}
dialog {
padding: 0;
}
/*
Prevent resizing textareas horizontally by default.
*/
textarea {
resize: vertical;
}
/*
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
2. Set the default placeholder color to the user's configured gray 400 color.
*/
input::placeholder,
textarea::placeholder {
opacity: 1; /* 1 */
color: #9ca3af; /* 2 */
}
/*
Set the default cursor for buttons.
*/
button,
[role="button"] {
cursor: pointer;
}
/*
Make sure disabled buttons don't get the pointer cursor.
*/
:disabled {
cursor: default;
}
/*
1. Make replaced elements \`display: block\` by default. (https://github.com/mozdevs/cssremedy/issues/14)
2. Add \`vertical-align: middle\` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
This can trigger a poorly considered lint error in some tools but is included by design.
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block; /* 1 */
vertical-align: middle; /* 2 */
}
/*
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/
img,
video {
max-width: 100%;
height: auto;
}
/*
Make elements with the HTML hidden attribute stay hidden by default.
*/
[hidden]:where(:not([hidden="until-found"])) {
display: none;
}
`;
function compressCSS(css) {
return css.replace(/\s+/g, " ").replace(/\/\*[\s\S]*?\*\//g, "");
}
const resetPreflight = {
getCSS: () => compressCSS(resetCSS),
layer: layerMeta.layer
};
//#endregion
//#region src/core/preflights/index.ts
function preflights(options) {
if (typeof options.preflights === "boolean") return options.preflights ? [resetPreflight].filter(Boolean) : [];
return [options.preflights.reset ? resetPreflight : void 0].filter(Boolean);
}
//#endregion
//#region src/core/rules.ts
const rules = [[/^([^:]+)::(\S|[^:]+)$/, ([, k, v]) => {
return { [`--${k}`]: v };
}]];
normalizeRuleMeta(layerMeta);
function normalizeRuleMeta(ruleMeta) {
for (const r of rules) r[2] = Object.assign(r[2] || {}, ruleMeta);
}
//#endregion
//#region src/core/shortcuts.ts
const _shortcuts = [
["pr", "relative"],
["pa", "absolute"],
["pf", "fixed"],
["ps", "sticky"],
[["pxc", "p-x-c"], "pa left-1/2 -translate-x-1/2"],
[["pyc", "p-y-c"], "pa top-1/2 -translate-y-1/2"],
[[
"pcc",
"pc",
"p-c",
"p-c-c"
], "pxc pyc"],
[["fcc", "f-c"], "flex justify-center items-center"],
[["fccc", "f-c-c"], "f-c flex-col"],
[[
"fc",
"fxc",
"f-x-c"
], "flex justify-center"],
[[
"fi",
"fyc",
"f-y-c"
], "flex items-center"],
[["fs", "fxs"], "flex justify-start"],
["fsc", "flex justify-start items-center"],
["fse", "flex justify-start items-end"],
[["fe", "fxe"], "flex justify-end"],
["fec", "flex justify-end items-center"],
["fb", "flex justify-between"],
["fbc", "flex justify-between items-center"],
["fa", "flex justify-around"],
["fac", "flex justify-around items-center"],
["fw", "flex flex-wrap"],
["fwr", "flex flex-wrap-reverse"],
["trans", "transition-all-350 ease-DEFAULT"]
];
const shortcuts = normalizeShortcut(_shortcuts);
normalizeShortcutMeta(layerMeta);
function normalizeShortcutMeta(ruleMeta) {
for (const r of shortcuts) r[2] = Object.assign(r[2] || {}, ruleMeta);
}
function normalizeShortcut(shortcut) {
return shortcut.flatMap((s) => {
if (Array.isArray(s[0])) return s[0].map((i) => [
i,
s[1],
s[2]
]);
return [s];
});
}
//#endregion
//#region src/core/theme/index.ts
function nomarlizeTheme(theme) {
return { ...theme };
}
//#endregion
//#region src/core/variants/active.ts
const v_active = {
name: "@active",
match(matcher) {
if (!matcher.startsWith("@active")) return matcher;
return {
matcher: matcher.slice(8),
selector: (s) => `${s}.active`
};
}
};
//#endregion
//#region src/core/variants/index.ts
function variants(_options) {
return [v_active];
}
//#endregion
//#region src/utils.ts
const animationRegExp = /^([a-z-]+)\s+([0-9.]+m?s?|[*+])?\s?([a-z-]+(?:\([^)]+\))?|[*+])?\s*([a-z0-9-]+|[*+])?$/i;
/**
* Normalize custom animate usage to UnoCSS animations theme.
*
* ⚠️ You must strictly follow the following format. ⚠️
*
* [ name, duration, timing-function, iteration-count ]
*
* If you use * as placeholder, it will be ignored.
*
* [name, duration, *, iteration-count]
*
* If you use + as placeholder, it will be replaced with empty string.
*
* [name, duration, +, iteration-count]
*
* @example
*
* { animate: ['spin 1s linear infinite'] }
*
* Will be transformd:
*
* {
* animate: ['spin 1s linear infinite'],
* durations: {
* spin: '1s',
* },
* timingFns: {
* spin: 'linear',
* },
* counts: {
* spin: 'infinite',
* },
* }
*/
function resolveAnimation(extend_animation) {
const animation = {};
const keys = [
"durations",
"timingFns",
"counts"
];
const shortcuts$1 = [];
for (const k in extend_animation) {
const v = extend_animation[k];
const match = v.match(animationRegExp);
if (match != null) {
const [, name, duration, timing, count] = match;
const values = [
duration,
timing,
count
];
if (name !== k) shortcuts$1.push([`animate-${k}`, `animate-${name}`]);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = values[i];
if (value != null) {
if (value === "*") continue;
if (animation[key]) animation[key][name] = values[i] === "+" ? "" : values[i];
else animation[key] = { [name]: values[i] === "+" ? "" : values[i] };
}
}
}
}
return {
animation,
shortcuts: shortcuts$1
};
}
/**
* Deep merge two objects.
* @param original Original object
* @param patch Patch object
* @returns Merged object
*/
function deepMerge(original, patch) {
const o = original;
const p = patch;
if (Array.isArray(o) && Array.isArray(p)) return [...o, ...p];
if (Array.isArray(o)) return [...o];
const output = { ...o };
if (isObject(o) && isObject(p)) Object.keys(p).forEach((key) => {
if (isObject(p[key])) if (!(key in o)) Object.assign(output, { [key]: p[key] });
else output[key] = deepMerge(o[key], p[key]);
else Object.assign(output, { [key]: p[key] });
});
return output;
}
function isObject(val) {
return val !== null && typeof val === "object";
}
function getKeyframes(css) {
const root = postcss.parse(css);
const obj = objectify(root);
return Object.keys(obj).reduce((acc, key) => {
if (key.startsWith("@keyframes")) acc[key] = obj[key];
return acc;
}, {});
}
function cssObj2StrSync(style) {
return Object.keys(style).reduce((str, key) => {
return `${str}${key}${stringifyObj(style[key])}`;
}, "").replace(/\n/g, "");
}
function stringifyObj(obj) {
return `{${Object.keys(obj).reduce((str, key) => {
return `${str}${camelToHyphen(key)}:${obj[key]};`;
}, "")}}`;
}
async function cssObj2StrAsync(style) {
return postcss().process(style, { parser: postcssJs }).then((result) => result.css);
}
function toArray(val) {
return Array.isArray(val) ? val : [val];
}
function camelToHyphen(str) {
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}
//#endregion
//#region src/resolve.ts
const defaultOptions = {
theme: {},
postprocess: { important: false },
shortcuts: { default: true },
preflights: { reset: false },
wind4: true,
wind3: false,
attributify: true,
icons: true,
webFonts: false,
typography: false,
tagify: false,
remToPx: false,
magicss: false,
directives: true,
variantGroup: true,
compileClass: false
};
const defaultPresetOptions = { webFonts: { provider: "fontsource" } };
const defaultPostprocessOptions = {
important: {
excludes: [],
includes: [/.*/g]
},
unColor: "--un-color"
};
async function resolveOptions(options) {
const optionsWithDefault = Object.assign({}, defaultOptions, options);
if (optionsWithDefault.wind4 && optionsWithDefault.wind3) {
console.warn("wind3 and wind4 are mutually exclusive, only one can be enabled");
optionsWithDefault.wind3 = false;
}
if (optionsWithDefault.postprocess === true) optionsWithDefault.postprocess = defaultPostprocessOptions;
else if (typeof optionsWithDefault.postprocess === "object") {
if (typeof optionsWithDefault.postprocess.important === "object") optionsWithDefault.postprocess.important = {
...defaultPostprocessOptions.important,
...optionsWithDefault.postprocess.important
};
else if (optionsWithDefault.postprocess.important === true) optionsWithDefault.postprocess.important = defaultPostprocessOptions.important;
if (optionsWithDefault.postprocess.unColor === true) optionsWithDefault.postprocess.unColor = defaultPostprocessOptions.unColor;
}
const presets = await resolvePresets(optionsWithDefault);
const transformers = await resolveTransformers(optionsWithDefault);
const { theme: t_theme, shortcuts: shortcuts$1 } = resolveExtend(optionsWithDefault.theme.extend ?? {});
const _theme = deepMerge(optionsWithDefault.theme, t_theme);
return {
...optionsWithDefault,
theme: nomarlizeTheme(_theme),
meta: {
presets,
shortcuts: shortcuts$1,
transformers
}
};
}
function resolveExtend(extend) {
const _shortcuts$1 = [];
const { animation, keyframes } = extend;
const { animation: resolvedAnimation, shortcuts: shortcuts$1 } = resolveAnimation(animation ?? {});
_shortcuts$1.push(...shortcuts$1);
resolvedAnimation.keyframes = {};
for (const key in keyframes) resolvedAnimation.keyframes[key] = `{${cssObj2StrSync(keyframes[key])}}`;
return {
theme: { animation: resolvedAnimation },
shortcuts: _shortcuts$1
};
}
async function resolvePresets(options) {
const presets = [];
const presetMap = {
wind3: import("unocss").then((m) => m.presetWind3),
wind4: import("unocss").then((m) => m.presetWind4),
attributify: import("unocss").then((m) => m.presetAttributify),
icons: import("unocss").then((m) => m.presetIcons),
webFonts: import("unocss").then((m) => m.presetWebFonts),
typography: import("unocss").then((m) => m.presetTypography),
tagify: import("unocss").then((m) => m.presetTagify),
remToPx: import("./dist-CncyueeJ.js").then((m) => m.presetRemToPx),
magicss: import("unocss-preset-magicss").then((m) => m.presetMagicss)
};
for (const [key, preset] of Object.entries(presetMap)) {
const option = options[key];
if (option) {
const p = await preset;
const presetOptions = defaultPresetOptions[key];
if (typeof option === "object") presets.push(p({
...presetOptions,
...option
}));
else presets.push(p(presetOptions ?? {}));
}
}
return presets;
}
async function resolveTransformers(options) {
const transformers = [];
const transformerMap = {
directives: import("unocss").then((m) => m.transformerDirectives),
variantGroup: import("unocss").then((m) => m.transformerVariantGroup),
compileClass: import("unocss").then((m) => m.transformerCompileClass)
};
for (const [key, transformer] of Object.entries(transformerMap)) {
const option = options[key];
if (option) {
const t = await transformer;
transformers.push(t(typeof option === "boolean" ? {} : option));
}
}
return transformers;
}
//#endregion
//#region src/index.ts
const presetUseful = definePreset(async (options) => {
const resolvedOptions = await resolveOptions(options ?? {});
const { theme, meta, shortcuts: userShortcuts } = resolvedOptions;
const shortcuts$1 = userShortcuts === true || typeof userShortcuts === "object" && userShortcuts.default !== false ? shortcuts : [];
return {
name: `unocss-preset-${PRESET_NAME}`,
layers: { [PRESET_NAME]: 2 },
rules,
theme,
variants: variants(resolvedOptions),
shortcuts: shortcuts$1,
extractors,
postprocess: postprocess(resolvedOptions),
presets: meta.presets,
transformers: meta.transformers,
preflights: preflights(resolvedOptions),
options: resolvedOptions
};
});
var src_default = presetUseful;
function defineConfig(config) {
return config;
}
function defineUsefulConfig(options = {}, config = {}) {
return mergeConfigs([defineConfig({ presets: [presetUseful(options)] }), config]);
}
//#endregion
export { animationRegExp, camelToHyphen, cssObj2StrAsync, cssObj2StrSync, deepMerge, src_default as default, defineConfig, defineUsefulConfig, getKeyframes, isObject, presetUseful, resolveAnimation, stringifyObj, toArray };