Skip to content

unpreset/unocss-preset-useful

Repository files navigation

unocss-preset-useful npm

Integrate and useful preset.

Features

  • 🔥 All-in-One popular presets.
  • 🚀 Collection of features not integrated into UnoCSS.
    • 🍥 Support extract base64 image.
    • 🎨 Support extract rgba color in css variable.
    • 💜 Support expand theme animation name usage.
    • 🍬 etc.
  • 📦 Build-In Magic Animate.
  • 🌬️ Align with TW theme configuration.

Usage

pnpm i -D unocss-preset-useful unocss
// unocss.config.ts
import { defineUsefulConfig } from 'unocss-preset-useful'

export default defineUsefulConfig()

// Custom options
export default defineUsefulConfig(
  {
    // Useful options
  },
  {
    // Uno options
  }
)
Options
export interface UsefulOptions {
  /**
   * Make all unitilities important.
   *
   * @default false
   */
  important?: boolean

  /**
   * Enable default shortcuts
   *
   * @default true
   */
  enableDefaultShortcuts?: boolean

  /**
   * Enable magic animations
   *
   * @default true
   */
  enableMagicAnimations?: boolean

  /**
   * Extract rgba color in css variable
   *
   * @default false
   */
  unColor?: boolean | string

  /**
   * Improve theme to be more useful, and align with Tailwind theme configuration
   *
   * - Add `animation` to theme, Expand theme animation name usage
   *
   * [ name, duration, timing-function, iteration-count ]
   *
   * @example
   *
   * ```ts
   * theme: {
   *   extend: {
   *     animation: {
   *      shape: 'shape 5s linear infinite'
   *     },
   *     // ...
   *   }
   * }
   * ```
   * You can choose to use special symbols as placeholders, to indicate whether to inject this property into the uno theme
   *
   * - `*` Abandon injection
   * - `+` Injection, but the value is empty
   *
   * @example
   *
   * ```ts
   * theme: {
   *   extend: {
   *     animation: {
   *      foo: 'foo 1s * 3',
   *      bar: 'bar 1s +',
   *     },
   *     // ...
   *   }
   * }
   * ```
   *
   */
  theme?: UsefulTheme

  /**
   * Enable the default preset
   * Only works when `presets` is not specified
   * @default true
   */
  uno?: boolean | PresetUnoOptions

  /**
   * Enable attributify mode and the options of it
   * Only works when `presets` is not specified
   * @default false
   */
  attributify?: boolean | AttributifyOptions

  /**
   * Enable icons preset and the options of it
   * Only works when `presets` is not specified
   * @default false
   */
  icons?: boolean | IconsOptions

  /**
   * Enable webFonts preset and the options of it
   * Only works when `presets` is not specified
   * @default false
   */
  webFonts?: boolean | WebFontsOptions

  /**
   * Enable typography preset and the options of it
   * Only works when `presets` is not specified
   * @default false
   */
  typography?: boolean | TypographyOptions

  /**
   * Enable tagify preset and the options of it
   * Only works when `presets` is not specified
   * @default false
   */
  tagify?: boolean | TagifyOptions

  /**
   * Enable remToPx preset and the options of it
   * Only works when `presets` is not specified
   * @default false
   */
  remToPx?: boolean | RemToPxOptions

  /**
   * Enable scrollbar preset and the options of it
   * Only works when `presets` is not specified
   *
   * See: https://github.com/action-hong/unocss-preset-scrollbar
   *
   * @default false
   */
  scrollbar?: boolean | PresetScrollbarDefaultOption
}


Details

Expand it see more details

autocomplete

export const autocomplete: UserConfig['autocomplete'] = {
  shorthands: {
    magic: `(${Object.keys(keyframes).join('|')})`,
  },
  templates: [
    'animate-<magic>',
  ],
}

extractors

// https://github.com/unocss/unocss/pull/2485
// Support extract base64 image.
export const extractors: Extractor[] = [
  {
    name: 'unocss-preset-useful-extractor-includes-base64',
    order: 0,
    extract({ code }) {
      return [...new Set(code.split(/[\\:]?[\s'"`{}]|;(?!base64)+/g))]
    },
  },
]

postprocess

// https://github.com/unocss/unocss/discussions/2816
// Extract rgba color in css variable.
export function postprocessWithUnColor(unColor: string): Postprocessor {
  return (util) => {
    util.entries.forEach((i) => {
      const value = i[1]
      if (typeof value === 'string') {
        const match = value.match(rgbaRE)
        if (match != null) {
          i[1] = value.replace(rgbaRE, `rgba(var(${unColor}),$2)`)
          util.entries.unshift([unColor, match[1]])
        }
      }
    })
  }
}

export function importantProcess(): Postprocessor {
  return (util) => {
    util.entries.forEach((i) => {
      if (i[1] != null && !String(i[1]).includes('!important'))
        i[1] += ' !important'
    })
  }
}

rules

// Use any css variable easily.
export const rules: Rule[] = [
  [/^(.+)::(.+)$/, ([, n, v], { theme }) => {
    const color = parseColor(v, theme)
    if (color?.cssColor?.type === 'rgb' && color.cssColor.components) {
      return {
        [`--${n}`]: `${color.cssColor.components.join(',')}`,
      }
    }
    return {
      [`--${n}`]: v,
    }
  }],
]

shortcuts

// FYI. My own shortcuts.
const _shortcuts: CustomStaticShortcuts = [
  // position
  ['pr', 'relative'],
  ['pa', 'absolute'],
  ['pf', 'fixed'],
  ['ps', 'sticky'],

  // position layout
  [['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'],

  // flex layout
  [['f-c', 'fcc'], 'flex justify-center items-center'],
  [['f-c-c', 'fccc'], 'f-c flex-col'],
  [['fc', 'fxc', 'f-x-c'], 'flex justify-center'],
  [['fi', 'fyc', 'f-y-c'], 'flex items-center'],
  ['fs', 'flex justify-start'],
  ['fsc', 'flex justify-start items-center'],
  ['fse', 'flex justify-start items-end'],
  ['fe', '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'],

  // transition
  ['trans', 'transition-all-350 ease-linear'],
]

index

// See index.test.ts `themeAnimate configuration` for usage.
export function nomarlizeTheme(theme: UsefulTheme, enableMagicAnimations: boolean): UsefulTheme {
  return {
    ...theme,
    animation: deepMerge(
      enableMagicAnimations ? MagicAnimation : {},
      theme.animation ?? {},
    ),
  }
}

magic-animate

export function magicAnimate(): Theme['animation'] {
  const keyframesObj = getKeyframes(magicCSS)

  function generate<T = string>(val?: T): Record<string, T> {
    return Object.keys(keyframesObj).reduce((acc, key) => {
      const name = key.replace('@keyframes ', '')
      // @ts-expect-error nothing
      acc[name] = val ?? `{${cssObj2StrSync(keyframesObj[key])}}`
      return acc
    }, {})
  }

  return {
    keyframes: generate(),
    durations: generate('1s'),
    properties: generate({ 'animation-fill-mode': 'both' }),
  }
}


License

MIT License © 2022 zyyv