npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@anoyomoose/q2-fresh-paint-core

v0.1.3

Published

Quasar theming engine — Vite plugin for Sass-based theme overrides

Downloads

418

Readme

@anoyomoose/q2-fresh-paint-core

Quasar theming engine — a Vite plugin that applies Sass-based theme overrides to Quasar components. Supports multiple themes stacked in sequence.

This is a pure build-time package. It has no runtime browser code and no dependency on quasar or vue.

Installation & Quick Start

Install the core engine alongside a theme package:

# or equivalent for your package manager
pnpm add @anoyomoose/q2-fresh-paint-core @anoyomoose/q2-fresh-paint-md3e

Add freshPaint() to your quasar.config.js. In a Quasar project, Vite plugins are registered inside build.vitePlugins, and boot files (runtime JS that runs at app startup) go in the boot array. The ~ prefix tells Quasar to resolve from node_modules:

import { freshPaint } from '@anoyomoose/q2-fresh-paint-core'
import { md3eTheme } from '@anoyomoose/q2-fresh-paint-md3e'

export default defineConfig({
  boot: [
    // Theme boot file — patches component prop defaults at runtime
    '~@anoyomoose/q2-fresh-paint-md3e/boot'
  ],

  build: {
    vitePlugins: [
      // Theme engine — intercepts Sass compilation to inject theme overrides
      freshPaint({
        themes: [ md3eTheme({ sourceColor: '#6750a4' }) ],
      })
    ]
  }
})

freshPaint() must appear in build.vitePlugins after Quasar's own Vite plugin (which Quasar registers automatically, so just adding it to the array is sufficient). Boot files are separate because they're a Quasar runtime concept — the Vite plugin only handles build-time Sass injection.

API Reference

freshPaint(options: FreshPaintOptions): Plugin

Returns a Vite plugin that intercepts Quasar's Sass compilation to inject theme overrides.

FreshPaintOptions

| Property | Type | Default | Description | |---|---|---|---| | themes | ThemeDescriptor[] | (required) | Ordered array of themes to apply. | | userThemeDir | string | 'src/theme' | Base directory for user overrides, relative to app root. |

ThemeDescriptor

Describes a single theme. Theme packages export a factory function that returns this object.

interface ThemeDescriptor {
  /** Unique name — used for generated vars filename and user override dir resolution */
  name: string
  /** Absolute path to theme's SCSS directory (variables.scss, base.scss, components/) */
  dir: string
  /** Optional: returns Sass variable content, written to .quasar/theme.<name>.scss */
  generateVariables?: () => string
}

| Field | Description | |---|---| | name | Unique identifier. Determines the user override subdirectory (src/theme/<name>/) and the generated variables filename (.quasar/theme.<name>.scss). | | dir | Absolute path to the directory containing the theme's SCSS files. Must contain some combination of variables.scss, base.scss, and a components/ subdirectory. | | generateVariables | Optional function called at build time. Its return value (a string of Sass content) is written to .quasar/theme.<name>.scss and included in the variable import chain. Useful for computed values like palette generation from a source color. |

Building Themes

This section explains how to author a theme package for Fresh Paint.

Directory Structure

A theme package provides a directory with these files:

theme/
  variables.scss        — Sass variable overrides (using !default)
  base.scss             — global styles, CSS custom properties, utility classes
  components/
    QBtn.scss           — override for QBtn
    QCard.scss          — override for QCard
    ...

All files are optional. Include only what the theme needs to override.

The theme package exports a factory function that returns a ThemeDescriptor pointing to this directory:

import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import type { ThemeDescriptor } from '@anoyomoose/q2-fresh-paint-core'

export function myTheme(): ThemeDescriptor {
  const pkgDir = dirname(fileURLToPath(import.meta.url))
  return {
    name: 'my-theme',
    dir: resolve(pkgDir, 'theme'),
  }
}

Component Override Resolution

Using the QBtn component as example:

Override files are placed in theme/components/QBtn.scss. The filename must match the Quasar component name exactly, with either .scss or .sass extension.

In development mode the engine registers a custom Sass importer that intercepts Quasar's own component files. When Sass resolves a path like components/QBtn/QBtn.sass, the importer:

  1. Reads the original component Sass content
  2. Appends @import statements for each theme's override file
  3. Returns the combined content to Sass

The original file's directory context is preserved, so Quasar's own @import statements within the component still resolve correctly. The override file is appended after the original, so its CSS rules take precedence by source order.

In distribution mode it doesn't actually matter. When the distribution sass is seen by the transform hook, it appends all the theme CSS. However, it is still a good structure to follow.

Development mode

One way to develop themes is to have the full Quasar source available, and install your theme package in link mode with (p)npm. That way the importer will do its job slightly differently and you can run Quasar source's built-in UI playground to systematically check your work. You will still have to constantly rebuild your package and restart the dev server for various changes to take effect, but it is still an easy way to work.

Of course, if you're developing inside your own web project, you can create a very basic package and then use "user overrides" (see below) to style any component directly from your tree, before copying the final version into your actual package. You'll likely still have to restart the dev server regularly.

Variable Priority

Quasar's built-in Sass variables all use the !default flag, which means the first definition wins. The engine exploits this by prepending theme variable imports before Quasar's own variable file.

The engine provides three user override points for variables, each at a different position in the import chain. For a single theme, the full import order in each Sass file is:

  1. quasar.variables.scss — the project's own Quasar variable file (prepended by Quasar's Vite plugin)
  2. User variables.pre.scss — before generated variables
  3. Generated variables from generateVariables() (if provided)
  4. User variables.scss — after generated, before package
  5. Theme package variables.scss
  6. User variables.post.scss — after package variables
  7. Quasar's variables.sass with !default (lowest priority)

Since !default means "only set if not yet defined," earlier definitions win for !default variables. Hard assignments (no !default) always take effect regardless of position.

The three user variable files serve different purposes:

  • variables.pre.scss — imported before the generated palette. Use this to override individual generated tokens (e.g., force $md3-primary to a specific value while letting the rest of the palette generate normally). Use !default so quasar.variables.scss can still win.

  • variables.scss — imported after generated variables but before the package's own variables. Can reference generated tokens (e.g., $md3-primary). Use !default to override the package's default mappings while preserving the fallback chain.

  • variables.post.scss — imported after everything. Can reference all tokens from all sources — generated palette, package variables, shape tokens, motion tokens, everything. Use hard assignments (no !default) here, since all variables are already defined by this point.

All three files are optional. Most users will only need variables.post.scss (to tweak fully-resolved values) or variables.scss (to remap how generated tokens are applied).

Important for theme users: Quasar's quasar.variables.scss is loaded before any theme variables, and typically uses hard assignments (no !default). Any variable defined there — such as $primary: #1976D2 — will override the theme's value for that variable. Theme packages should document which Quasar variables they manage, so users know what to remove from quasar.variables.scss.

For theme authors: always use !default in your package's variables.scss so that user overrides at every level can take priority:

// In a theme's variables.scss
$primary: $my-generated-primary !default;
$button-border-radius: 20px !default;

Base File Injection

base.scss is appended to Quasar's main CSS entry point (the file that imports all components). Use it for:

  • CSS custom properties (e.g., --md3-primary: #{$md3-primary})
  • Global styles and resets
  • Utility classes
// In a theme's base.scss
:root {
  --my-theme-primary: #{$primary};
  --my-theme-radius: 12px;
}

.body--dark {
  --my-theme-primary: #{$primary-dark};
}

generateVariables()

An optional function on ThemeDescriptor that returns a string of Sass variable content. Called at build time; output is written to .quasar/theme.<name>.scss.

This is useful when variable values need to be computed — for example, generating an entire color palette from a single source color:

export function myTheme(options: { color: string }): ThemeDescriptor {
  return {
    name: 'my-theme',
    dir: resolve(__dirname, 'theme'),
    generateVariables() {
      const palette = generatePalette(options.color)
      return `$my-primary: ${palette.primary} !default;\n`
        + `$my-secondary: ${palette.secondary} !default;\n`
    },
  }
}

Generated variables sit between user overrides and package variables in priority order, so users can still override them.

Boot Files

Some theme behavior cannot be expressed in CSS alone — for example, setting default component props (noCaps, unelevated) or attaching DOM observers. These are handled by boot files: runtime JavaScript that runs when the app starts.

Boot files are exported separately from the theme package (e.g., @anoyomoose/q2-fresh-paint-md3e/boot) and registered by the user in the quasar.config.js boot array. The core engine does not manage boot files — they are a convention between the theme package and the user.

// quasar.config.js
boot: [
  '~@anoyomoose/q2-fresh-paint-md3e/boot'
]

Multi-Theme Stacking

Multiple themes can be applied simultaneously by passing them in the themes array. The order matters.

Given themes: [themeA, themeB]:

Variables (first !default definition wins; hard assignments always take effect):

  1. themeA variables.pre.scss
  2. themeA generated variables
  3. themeA variables.scss
  4. themeA package variables
  5. themeA variables.post.scss
  6. themeB variables.pre.scss
  7. themeB generated variables
  8. themeB variables.scss
  9. themeB package variables
  10. themeB variables.post.scss
  11. Quasar defaults (lowest priority)

Earlier themes have higher !default priority. Hard assignments in variables.post.scss override everything above them.

Component overrides (appended to original — later CSS wins by source order):

  1. Original component content
  2. themeA override (user file replaces package file within this theme)
  3. themeB override (user file replaces package file within this theme)

Later themes' CSS rules override earlier themes' rules.

Base files (appended to main entry — later CSS wins by source order):

  1. themeA base (user file replaces package file within this theme)
  2. themeB base (user file replaces package file within this theme)

This means variables and CSS rules have opposite priority directions: for variables, earlier themes win; for CSS rules, later themes win.

User Overrides

Users can override any theme file without forking the theme package.

Configuration

The base directory defaults to src/theme (relative to app root). Override it with:

freshPaint({
  themes: [md3eTheme()],
  userThemeDir: 'src/my-overrides',
})

Directory Layout

For each theme, place overrides in a subdirectory matching the theme's name:

src/theme/<themeName>/
  variables.pre.scss      — before generated palette (optional)
  variables.scss          — after generated, before package (optional)
  variables.post.scss     — after package variables (optional)
  base.scss               — replaces the theme's base stylesheet
  components/
    QBtn.scss             — replaces the theme's QBtn override
    QCard.scss

All files are optional. Only include the files you need.

Override Behavior

User overrides are designed primarily to ease theme development and debugging — they let you iterate on a single component's styling or tweak variables without forking and rebuilding the theme package. They're also useful for project-specific adjustments, but their primary purpose is the development workflow.

The file types have different override semantics:

  • Variable files (variables.pre.scss, variables.scss, variables.post.scss) — extend. All user variable files, generated variables, and the package's variables are imported together. Each user file is inserted at a specific point in the chain (see Variable Priority). You only need to define the variables you want to change — the package's variables remain as fallbacks.

  • base.scss — replaces. If the user provides base.scss, the package's base.scss is not used at all. This gives you a clean slate for CSS custom properties and global styles. If you want to extend rather than replace, import the package file from within your override:

    // src/theme/md3e/base.scss
    @import '@anoyomoose/q2-fresh-paint-md3e/dist/theme/base.scss';
    
    // Additional global styles
    .my-custom-class { ... }
  • components/<Name>.scss — replaces. If the user provides QBtn.scss, the package's QBtn.scss is not used for that component. Same clean-slate approach — import the package file explicitly if you want to extend:

    // src/theme/md3e/components/QBtn.scss
    @import '@anoyomoose/q2-fresh-paint-md3e/dist/theme/components/QBtn.scss';
    
    // Additional overrides
    .q-btn { letter-spacing: 0.05em; }

License

MIT