# Vuetify Admin & Architecture Guide

This guide is for the engineers, architects, and maintainers who build **on** Vuetify or
contribute **to** it. Where the User Guide explains how to *use* components, this document
explains how the framework is *built*: its monorepo structure, the reactive
composable-injection architecture, the component composition pattern, the multi-stage build
pipeline, the code-generation tooling, the testing strategy, and the security and
performance characteristics of the codebase.

It is derived directly from the source at version `4.1.0` (commit `bbffe05`). It is detailed
enough that an engineer — or an AI — could reconstruct the framework's architecture from
this document alone.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [Repository Architecture](#2-repository-architecture)
3. [The `createVuetify` Plugin: Initialization Flow](#3-the-createvuetify-plugin-initialization-flow)
4. [Component Composition Architecture](#4-component-composition-architecture)
5. [The Composables Layer](#5-the-composables-layer)
6. [Theming Internals](#6-theming-internals)
7. [The Build Pipeline](#7-the-build-pipeline)
8. [The API Generator](#8-the-api-generator)
9. [Configuration Reference](#9-configuration-reference)
10. [Styling & SASS System](#10-styling--sass-system)
11. [Testing Strategy](#11-testing-strategy)
12. [Security Analysis](#12-security-analysis)
13. [Performance & Bundle Strategy](#13-performance--bundle-strategy)
14. [Development & Operations](#14-development--operations)
15. [Contributing](#15-contributing)
16. [Known Issues & Improvement Opportunities](#16-known-issues--improvement-opportunities)
17. [Quick Reference](#17-quick-reference)

---

## 1. Executive Summary

Vuetify is a **Vue 3 Material Design component framework** distributed as the `vuetify` npm
package (v4.1.0, MIT). It comprises 104 stable components, 10 experimental "labs"
components, 8 directives, ~63 composables, 15 icon sets, 43 locales, and a CSS/SASS design
system.

**Architectural thesis.** The framework is organized around a single idea: *one plugin
builds a set of reactive subsystems and provides them through Vue's dependency-injection,
and every component consumes those subsystems through small, composable hooks.* There is no
central store and no class hierarchy — state lives in `effectScope`-bound reactive objects
keyed by `InjectionKey` symbols.

| Attribute | Value |
|-----------|-------|
| Package | `vuetify` |
| Version | `4.1.0` |
| License | MIT |
| Required Vue | `^3.5.0` |
| Node (tooling) | `>=24.11.1` |
| Module type | ESM (`"type": "module"`) |
| Package manager | `pnpm@10.26.1` (workspace monorepo) |
| Language | TypeScript + TSX (JSX render functions) |
| Build outputs | `lib/` (per-file ESM), `dist/` (bundled UMD/ESM), type declarations |

**Workspace packages:** the public `vuetify` library, the `@vuetify/api-generator` tooling,
and `vuetifyjs.com` (the documentation site).

---

## 2. Repository Architecture

Vuetify is a **pnpm workspace monorepo** with three packages under `packages/`:

```
vuetify/  (root: @vuetify/vuetify-root, private)
├── packages/
│   ├── vuetify/            The published framework (npm: "vuetify")
│   │   ├── src/
│   │   │   ├── framework.ts        createVuetify() — the heart
│   │   │   ├── entry-bundler.ts    CDN/UMD entry (bundles all components)
│   │   │   ├── components/         104 components (VBtn, VTextField, …)
│   │   │   ├── labs/               10 experimental components
│   │   │   ├── composables/        ~63 reactive hooks (theme, display, …)
│   │   │   ├── directives/         8 directives (ripple, intersect, …)
│   │   │   ├── blueprints/         md1 / md2 / md3 preset bundles
│   │   │   ├── iconsets/           15 icon-set adapters
│   │   │   ├── locale/             43 language files + 2 adapters
│   │   │   ├── styles/             SASS: settings, tools, generic, utilities
│   │   │   ├── util/               Helpers (color, box, propsFactory, …)
│   │   │   └── icons.ts, types.ts  Public type surface
│   │   ├── build/                  Rollup configs + babel plugins
│   │   ├── dev/                    Local playground app
│   │   └── test/                   Test setup, templates, browser commands
│   ├── api-generator/      Generates the per-component API docs (ts-morph)
│   └── docs/               vuetifyjs.com (Vite + vue-router + i18n + Algolia)
└── scripts/                Repo-wide build/dev/release scripts
```

### Package boundaries

| Package | Published? | Role |
|---------|-----------|------|
| `vuetify` | **Yes** (public) | The framework consumers install. |
| `@vuetify/api-generator` | No (private) | Reads built types, emits API metadata for the docs. |
| `vuetifyjs.com` | No (private) | The documentation website, consuming `vuetify` via `workspace:*`. |

> [!NOTE]
> The docs site and api-generator depend on the library via `workspace:*`, so they always
> build against the local source. This is why a change to a component's types is reflected
> in the generated API docs without publishing.

### Module resolution

Inside `packages/vuetify`, the alias `@/` maps to `src/`. This is configured three ways so
every toolchain agrees: in `vite.config.ts` (`resolve.alias`), in `babel.config.js` (the
`module-resolver` plugin), and in `tsconfig.json` (`paths`). The package's `vite.config.ts`
additionally aliases bare `vuetify` imports to `src/framework.ts` so the dev playground and
tests resolve to source rather than the built library.

---

## 3. The `createVuetify` Plugin: Initialization Flow

`framework.ts` exports `createVuetify(options)`. Understanding its flow is the key to
understanding the whole framework.

```ts
export function createVuetify (vuetify: VuetifyOptions = {}) {
  const { blueprint, ...rest } = vuetify
  const options = mergeDeep(blueprint, rest)   // 1. merge blueprint under user options
  const { aliases = {}, components = {}, directives = {} } = options

  const scope = effectScope()                  // 2. own reactive scope
  return scope.run(() => {
    const defaults = createDefaults(options.defaults)   // 3. build subsystems
    const display  = createDisplay(options.display, options.ssr)
    const theme    = createTheme(options.theme)
    const icons    = createIcons(options.icons)
    const locale   = createLocale(options.locale)
    const date     = createDate(options.date, locale)
    const goTo      = createGoTo(options.goTo, locale)

    function install (app: App) { /* 4. register + provide */ }
    function unmount () { scope.stop() }

    return { install, unmount, defaults, display, theme, icons, locale, date, goTo }
  })!
}
```

### Step-by-step

1. **Blueprint merge.** If a `blueprint` is supplied, the user's options are deep-merged
   *over* it (`mergeDeep(blueprint, rest)`), so explicit options always win.
2. **Effect scope.** The whole framework runs inside an `effectScope()`, so all reactive
   effects (watchers, computeds) can be torn down at once via `unmount()`.
3. **Subsystem construction.** Seven factory functions build the reactive subsystems. Note
   the **dependency order**: `date` and `goTo` receive the already-built `locale` instance,
   because dates and scrolling are locale/RTL-aware.
4. **`install(app)`** runs when `app.use(vuetify)` is called. It:
   - Registers each directive via `app.directive(key, …)`.
   - Registers each component via `app.component(key, …)`.
   - Registers **aliases** — clones of components under a new name (with `aliasName` set).
   - Installs the theme into its own child `effectScope` and stops it on app unmount.
   - **Provides** each subsystem under its `InjectionKey` symbol.
   - For SSR, defers the first `display.update()` until after mount/hydration (or hooks
     into Nuxt's `app:suspense:resolve`).
   - Adds a global mixin exposing the reactive `$vuetify` object (when the Options API is
     enabled).

### Injection symbols

Each subsystem is provided under a `Symbol.for(...)` injection key. Components retrieve them
with `inject()` inside their composables:

| Subsystem | Symbol | Consumer composable |
|-----------|--------|---------------------|
| Defaults | `vuetify:defaults` | `injectDefaults()` / `useDefaults()` |
| Display | `vuetify:display` | `useDisplay()` |
| Theme | `vuetify:theme` | `useTheme()` / `provideTheme()` |
| Icons | `vuetify:icons` | `useIcon()` |
| Locale | `vuetify:locale` | `useLocale()` / `useRtl()` |
| Date | `vuetify:date-options`, `vuetify:date-adapter` | (date components) |
| GoTo | `vuetify:goto` | `useGoTo()` |

> [!IMPORTANT]
> Because state is provided through Vue injection, **provider components** like
> `<v-theme-provider>`, `<v-locale-provider>`, and `<v-defaults-provider>` work by calling
> the matching `provide*` function to override the injected instance for their subtree. This
> is the same mechanism the root plugin uses — there is no special-casing.

---

## 4. Component Composition Architecture

Every Vuetify component follows the same pattern. Studying `VBtn` reveals the template all
others follow.

### `propsFactory` + `make*Props` mixins

Shared prop groups are defined once as factory functions (`makeBorderProps`,
`makeDensityProps`, `makeElevationProps`, `makeRoundedProps`, `makeSizeProps`,
`makeTagProps`, `makeThemeProps`, `makeVariantProps`, `makeLocationProps`,
`makePositionProps`, `makeRouterProps`, `makeLoaderProps`, `makeGroupItemProps`,
`makeDimensionProps`, `makeComponentProps`, …). A component composes its props by spreading
them:

```ts
export const makeVBtnProps = propsFactory({
  active: { type: Boolean, default: undefined },
  block: Boolean,
  icon: [Boolean, String, Function, Object] as PropType<boolean | IconValue>,
  ripple: { type: [Boolean, Object], default: true },
  // …component-specific props…
  ...makeBorderProps(),
  ...makeComponentProps(),
  ...makeDensityProps(),
  ...makeElevationProps(),
  ...makeRoundedProps(),
  ...makeRouterProps(),
  ...makeSizeProps(),
  ...makeTagProps(),
  ...makeThemeProps(),
  ...makeVariantProps(),
  // …
}, 'VBtn')
```

`propsFactory(props, name)` returns a callable that produces the prop definitions and
supports prop **renaming/defaulting** at call sites.

### Composable hooks pair with the prop mixins

For every `make*Props()` there is a matching `use*()` composable that reads those props and
returns reactive classes/styles:

```ts
const { themeClasses } = provideTheme(props)
const { densityClasses } = useDensity(props)
const { elevationClasses } = useElevation(props)
const { roundedClasses, roundedStyles } = useRounded(props)
const { sizeClasses, sizeStyles } = useSize(props)
const { colorClasses, colorStyles, variantClasses } = useVariant(props)
const { dimensionStyles } = useDimension(props)
```

The render function then merges these into a single `class`/`style` binding. This is why a
prop like `elevation`, `rounded`, `density`, `color`, or `variant` behaves identically
across dozens of components — the behavior lives in one composable, not in each component.

### `genericComponent` / `defineComponent` + `useRender`

Components are declared with `genericComponent()` (which preserves generic slot typing) or
`defineComponent()`, and their render logic is registered with `useRender(() => <jsx/>)`.
Render functions are written in **TSX**, compiled by `@vuetify/babel-plugin-jsx`.

```ts
export const VBtn = genericComponent<VBtnSlots>()({
  name: 'VBtn',
  props: makeVBtnProps(),
  emits: { /* … */ },
  setup (props, { slots }) {
    const { themeClasses } = provideTheme(props)
    // …compose other hooks…
    useRender(() => withDirectives(
      <props.tag class={[ 'v-btn', themeClasses.value, /* … */ ]}>{ /* … */ }</props.tag>,
      [[vRipple, props.ripple]],
    ))
  },
})
```

> [!NOTE]
> Components import sibling components directly (e.g. `VBtn` imports `VIcon`,
> `VProgressCircular`, and `VDefaultsProvider`). The `index.ts` of each component folder
> re-exports the public surface; the package's `exports` map groups related components
> (e.g. all `VList*` under `vuetify/components/VList`).

### The defaults proxy

`useDefaults()` (in `composables/defaults.ts`) wraps the raw props in a `Proxy`. On every
prop read it resolves: **explicitly-set prop** → **component default** (`defaults[name]`) →
**global default** (`defaults.global`) → the prop's built-in default. It also detects
PascalCase keys in a component's defaults and provides them as *sub-component* defaults to
descendants. This is what makes `createVuetify({ defaults })` and `<v-defaults-provider>`
work transparently.

---

## 5. The Composables Layer

The `composables/` directory (~63 files) is the framework's true engine. They fall into
groups:

| Group | Composables | Responsibility |
|-------|-------------|----------------|
| **Framework state** | `theme`, `display`, `defaults`, `locale`, `icons`, `goto`, `date/` | The injected subsystems built by `createVuetify`. |
| **Visual props** | `border`, `density`, `dimensions`, `elevation`, `rounded`, `size`, `variant`, `color`, `position`, `location`, `tag` | Map shared props to classes/styles. |
| **Behavior** | `group`, `nested/`, `proxiedModel`, `form`, `validation`, `delay`, `focus`, `focusTrap`, `focusGroups`, `toggleScope` | Stateful interaction logic. |
| **DOM/observers** | `resizeObserver`, `intersectionObserver`, `mutationObserver`, `hydration`, `documentVisibility`, `ssrBoot` | Browser integration. |
| **Data** | `filter`, `virtual`, `list-items`, `selectLink`, `router` | Data tables, lists, virtual scrolling, routing. |
| **Specialized** | `calendar`, `dateFormat`, `mask/`, `hotkey/`, `scroll`, `touch`, `transition`, `loader`, `stack`, `teleport`, `layout` | Feature-specific. |

### Key cross-cutting composables

- **`proxiedModel`** — implements the `v-model` contract: a writable computed that reads
  `props.modelValue` and emits `update:modelValue`, supporting prop transforms. Nearly every
  input component uses it.
- **`group` / `nested`** — power selection across `VBtnToggle`, `VChipGroup`, `VItemGroup`,
  `VTabs`, `VList`, `VTreeview`, etc. `nested` provides pluggable **strategies** for active,
  open, and select behavior (`ActiveStrategy`, `OpenStrategy`, `SelectStrategy`).
- **`form` / `validation`** — `createForm` provides a `FormProvide` registry. Each input
  registers `{ validate, reset, resetValidation }`; the form aggregates child validity into
  a tri-state model (`true` / `false` / `null`). `validateOn` defaults to `'input'`;
  `fastFail` short-circuits at the first failure. `validate()` returns
  `{ valid, errors }`.
- **`layout`** — coordinates `VApp`, app bars, drawers, and footers. Each layout child
  registers its size and position; `layout` computes CSS variables (`--v-layout-top`, etc.)
  so `VMain` and `goTo`'s `layout` option can offset correctly.

---

## 6. Theming Internals

`composables/theme.ts` is one of the most intricate subsystems. It is worth a maintainer's
full attention.

### What `createTheme` builds

1. **Parsed options** merged over `genDefaults()` — which defines the full `light` and
   `dark` palettes and a set of `variables` (opacities, border/shadow colors, kbd/code
   colors, elevation overlay settings).
2. **`name`** — a computed that resolves `'system'` to the live OS preference (via
   `matchMedia('(prefers-color-scheme: dark)')`, updated on change).
3. **`computedThemes`** — for each theme it: merges onto the appropriate base
   (`light`/`dark`), generates color **variations** (lighten/darken shades), and computes
   **`on-` colors** automatically using luminance (`hasLightForeground`).
4. **`styles`** — a computed string of CSS wrapped in cascade layers.

### CSS architecture: cascade layers

The generated stylesheet uses `@layer` to control specificity deterministically:

```css
@layer vuetify-utilities {
  @layer theme-base { :root { --v-theme-primary: 24,103,192; … } .v-theme--dark { … } }
  @layer theme-background { .bg-primary { … } }
  @layer theme-foreground { .text-primary { … } .border-primary { … } }
}
```

Colors are emitted as **RGB component triples** (`--v-theme-primary: 24,103,192`) so
components can apply opacity with `rgb(var(--v-theme-primary), 0.5)`. Each non-`on-` color
also gets an `-overlay-multiplier` derived from its luminance, used by the elevation overlay
system.

### Stylesheet injection

`install(app)` injects the styles. If the app uses `@unhead/vue` or `@vueuse/head` (common
with Nuxt), the theme registers a head entry and patches it reactively. Otherwise it upserts
a `<style id="vuetify-theme-stylesheet">` element directly (respecting `cspNonce`). On the
server it renders once without watchers.

### Animated transitions

`change`/`toggle`/`cycle` optionally animate via the **View Transitions API**
(`document.startViewTransition`) with a circular clip-path reveal whose origin is set by
`setTransitionOrigin(event)`. It bails to an instant change when `prefers-reduced-motion` is
set or the API is unavailable.

> [!CAUTION]
> `theme.global.name.value = 'x'` is a **deprecated** assignment path (it logs a
> deprecation via a `Proxy`). Use `theme.change('x')` instead. This is the likely root of
> the upstream "inconsistent `toggle()` after enabling transitions" report — mixing the
> deprecated setter with the transition path can leave a stale `_transitionOrigin`.

### Scoping

`theme.scoped` and `theme.scope` allow restricting theme CSS to a selector (`:where(scope)`)
rather than `:root`, for embedding Vuetify inside a host app without leaking variables.

---

## 7. The Build Pipeline

The library has a multi-target build, orchestrated from `packages/vuetify/package.json`:

```bash
pnpm build
# = rimraf lib dist
#   && build:lib
#   && (build:dist & build:types:lib)   # concurrent
#   && build:types:bundle
```

| Step | Command | Output | Tool |
|------|---------|--------|------|
| `build:lib` | `babel src --out-dir lib …` | `lib/` — per-file ESM, mirrors `src/` | Babel |
| `build:dist` | `rollup -c build/rollup.config.js` | `dist/vuetify.js`, `.esm.js`, `.cjs`, labs bundles | Rollup |
| `build:types:lib` | `tsgo --emitDeclarationOnly -p tsconfig.dist.json` + `transform-types.js` | `lib/**/*.d.ts` | TypeScript (native preview `tsgo`) |
| `build:types:bundle` | `rollup -c build/rollup.types.config.js` | bundled `.d.ts` for `dist/` | rollup-plugin-dts |

### `lib/` vs `dist/`

- **`lib/`** is the primary published artifact (`main`/`module` → `lib/framework.js`). It is
  **per-file ESM**, which is what enables tree-shaking by the consumer's bundler. The Babel
  `lib` env applies two custom plugins: `babel-plugin-add-import-extension` (adds `.js`
  extensions) and `build/babel-plugin-replace-import-extension` (rewrites `.sass`/`.scss`
  imports to `.css`).
- **`dist/`** is the bundled UMD/ESM build for CDN usage (`vuetify.min.js`,
  `vuetify.esm.js`), produced by Rollup with Terser.

### `sideEffects` and tree-shaking

`package.json` declares `sideEffects: ["*.sass", "*.scss", "*.css", "*.vue"]`. This tells
bundlers that JS modules are side-effect-free (safe to drop if unused) while stylesheet
imports must be preserved. Combined with per-file `lib/` output and `vite-plugin-vuetify`'s
auto-import, only the components a consumer references end up in their bundle.

### The `exports` map

`package.json#exports` defines fine-grained entry points: `.`, `./styles`,
`./styles/core|colors|utilities`, `./framework`, `./blueprints/*`, `./components/*`,
`./directives/*`, `./locale`, `./locale/adapters/*`, `./iconsets/*`, `./labs/*`,
`./util/colors`, `./date/adapters/*`, and the `./dist/*` bundles. Editor tooling metadata
(`vetur`, `web-types`) points at generated JSON in `dist/json/`.

---

## 8. The API Generator

`@vuetify/api-generator` produces the exhaustive per-component API metadata that powers the
documentation site and editor IntelliSense. It does **not** ship to consumers.

### How it works

1. Reads the **built** library (`vuetify/dist/vuetify-labs.js`) and its generated
   `importMap.json` to enumerate every component.
2. Uses **`ts-morph`** to statically analyze the component/composable/directive TypeScript
   types and extract props, events, slots, and exposed members.
3. Parallelizes the heavy type analysis across worker threads with **Piscina** (thread count
   clamped to roughly half the CPU cores, scaled by free memory).
4. Merges human-written **descriptions** from per-locale data and reports any props missing
   a description.
5. Emits API JSON plus **Vetur** tags/attributes and **web-types** for IDE support.

CLI flags (from `yargs`): `--components <list>`, `--skip-directives`, `--skip-composables`.

```bash
cd packages/api-generator
node src/index.ts --components VBtn VCard   # generate for specific components
node src/index.ts --skip-composables
```

> [!NOTE]
> Because the generator reads *built types*, the documentation API is always a faithful
> reflection of the actual TypeScript signatures — there is no hand-maintained prop list
> that can drift from the code.

---

## 9. Configuration Reference

The `VuetifyOptions` interface (from `framework.ts`) is the complete configuration surface.

| Option | Type | Notes |
|--------|------|-------|
| `aliases` | `Record<string, Component>` | Register components under alternate names. |
| `blueprint` | `Blueprint` | A preset bundle of the options below; merged under user options. |
| `components` | `Record<string, Component>` | Components to register globally. |
| `directives` | `Record<string, Directive>` | Directives to register globally. |
| `defaults` | `DefaultsOptions` | Global + per-component default props. |
| `display` | `DisplayOptions` | `mobileBreakpoint`, `thresholds`. |
| `theme` | `ThemeOptions` | See below. |
| `icons` | `IconOptions` | `defaultSet`, `aliases`, `sets`. |
| `locale` | `LocaleOptions & RtlOptions` | `locale`, `fallback`, `messages`, `adapter`, `rtl`, separators. |
| `date` | `DateOptions` | `adapter`, `locale`, `formats`. |
| `goTo` | `GoToOptions` | `container`, `duration`, `offset`, `easing`, `layout`, `patterns`. |
| `ssr` | `boolean \| { clientWidth, clientHeight? }` | Enable SSR mode. |

### `ThemeOptions`

| Field | Default | Notes |
|-------|---------|-------|
| `defaultTheme` | `'system'` | `'light'`/`'dark'`/`'system'`/custom name. |
| `themes` | `{ light, dark }` | Map of theme definitions (`dark`, `colors`, `variables`). |
| `variations` | `{ colors: [], lighten: 0, darken: 0 }` | Auto-generate shades. |
| `transition` | `false` | `true` or `{ origin, duration }` for animated switches. |
| `utilities` | `true` | Emit `bg-`/`text-`/`border-` utility classes. |
| `stylesheetId` | `'vuetify-theme-stylesheet'` | ID of the injected `<style>`. |
| `cspNonce` | — | Nonce for the injected style element. |
| `scope` / `scoped` | — | Restrict theme CSS to a selector. |
| `prefix` | `'v-'` | CSS variable/class prefix. |

### `DisplayOptions`

| Field | Default |
|-------|---------|
| `mobileBreakpoint` | `'lg'` (1145px) |
| `thresholds` | `{ xs:0, sm:600, md:840, lg:1145, xl:1545, xxl:2138 }` |

### `GoToOptions`

| Field | Default |
|-------|---------|
| `duration` | `300` (ms) |
| `easing` | `'easeInOutCubic'` |
| `offset` | `0` |
| `layout` | `false` |
| `container` | document scrolling element |

### Build-time environment variables

These are read by the package's Vite/Vitest configs during development and CI — not at
runtime by consumers:

| Variable | Used in | Purpose |
|----------|---------|---------|
| `HOST`, `PORT` | `vite.config.ts` | Dev/preview server host/port (default port 8090). |
| `TEST` | `vite.config.ts` | Switches config into test mode (disables SSR plugin). |
| `VITE_SSR` | `vite.config.ts` | Enable the SSR dev server. |
| `BABEL_TYPES_8_BREAKING` | `vite.config.ts` | Babel compat flag (defined to `'false'`). |
| `TEST_TDD_ONLY`, `TEST_BAIL` | `vitest.config.ts` | TDD/visual-test toggles. |
| `GITHUB_ACTIONS` | `vitest.config.ts` | Selects CI reporters. |

Compile-time constants defined by Babel/Vite: `__VUETIFY_VERSION__`, `__REQUIRED_VUE__`,
`__VUE_OPTIONS_API__`.

---

## 10. Styling & SASS System

`src/styles/` is a structured SASS design system:

| Directory | Contents |
|-----------|----------|
| `settings/` | All SASS variables (colors, spacing scale, component variables). |
| `tools/` | Mixins and functions. |
| `generic/` | Resets and base element styles. |
| `utilities/` | Display, elevation, pointer-events, screen-reader, spacing helpers. |
| `elements/` | Base typographic/element styling. |
| `main.sass` | The aggregate entry compiled into `lib/styles/main.css`. |

Consumers can override variables by configuring the build plugin's `styles.configFile` and
`@use 'vuetify/settings' with (...)`. Three SASS entry points are exposed: `vuetify` (global
CSS), `vuetify/settings` (variables), and `vuetify/tools` (mixins).

> [!IMPORTANT]
> The precompiled `vuetify/styles` (CSS) cannot be customized. SASS customization requires
> the Vite/Webpack plugin so the SASS is recompiled with the consumer's overrides — this is
> the single most common source of "my variable override did nothing" confusion.

---

## 11. Testing Strategy

Testing runs on **Vitest 4** with two projects, configured in
`packages/vuetify/vitest.config.ts`:

| Project | Pattern | Environment | Purpose |
|---------|---------|-------------|---------|
| `unit` | `**/*.spec.{ts,tsx}` | `jsdom` | Fast logic/component unit tests. |
| `browser` | `**/*.spec.browser.{ts,tsx}` | Playwright (Chromium) | Real-browser interaction tests. |

Key points:

- The browser project uses `@vitest/browser-playwright` with a fixed `1280×800` viewport,
  reduced motion, and clipboard permissions for deterministic runs.
- **Visual regression** uses **Vizzly** (`@vizzly-testing/vitest`), configured in
  `vizzly.config.js` (1px threshold, signature on `theme` + `device`). A `tdd` workflow
  (`vizzly tdd start`) supports local snapshot iteration.
- `unplugin-auto-import` injects Vitest globals (`describe`, `it`, `expect`, `vi`, …) into
  spec files so they need no imports.
- Test scripts: `pnpm test` (all), `test:unit`, `test:browser`, `test:coverage` (Istanbul),
  and `test:all` which **shards** the browser suite into 5 parallel runs.

```bash
pnpm --filter vuetify test:unit          # fast feedback
pnpm --filter vuetify test:browser       # interaction tests
pnpm --filter vuetify test:coverage      # coverage report
```

> [!TIP]
> Component test templates live in `packages/vuetify/test/templates/`. The generator there
> throws *"Cannot generate examples from props without a component"* if invoked without a
> target — pass a component when scripting story/template generation.

---

## 12. Security Analysis

Vuetify is a **client-side UI library**. It has no server, no authentication, no network
layer, and no secret handling of its own. The relevant security surface is the DOM behavior
of its components and the safety of its style injection.

| Area | Assessment |
|------|------------|
| **Secrets** | None in the published package. The docs site's `.env.example` lists *its own* third-party keys (Cosmic.js, EmailJS, Algolia) — these belong to the website, not the framework, and ship empty. |
| **Style injection** | The theme writes a `<style>` element via `innerHTML` with framework-generated CSS only (color triples and variable names). It is not derived from user content, and it honors `cspNonce` for CSP compliance. |
| **XSS** | Components render through Vue's template/JSX system (escaped by default). As with any UI library, passing untrusted HTML into a slot or a component that renders raw HTML is the consumer's responsibility. |
| **Dependencies** | Runtime dependency surface is minimal — the public package's only true peer is Vue; date adapters and icon fonts are optional/peer. Heavy dependencies (Babel, Rollup, Playwright, ts-morph) are dev-only. |
| **Accessibility as security-adjacent** | ARIA correctness is actively maintained; e.g. an upstream fix added a missing `aria-label` to the treeview toggle (WCAG 4.1.2). |

> [!NOTE]
> When embedding Vuetify in a page with a strict Content Security Policy, set
> `theme.cspNonce` (and the same nonce on any icon-font CSS) so the injected stylesheet is
> permitted. Consider `theme.scoped` to avoid leaking `:root` variables into a host app.

> [!CAUTION]
> Treat untrusted strings rendered through components carefully. Vuetify escapes normal
> bindings, but components that intentionally render markup (rich text, code highlighting)
> will render whatever you give them — sanitize upstream.

---

## 13. Performance & Bundle Strategy

- **Tree-shaking first.** Per-file `lib/` ESM + `sideEffects` declaration + the Vite
  plugin's `autoImport` mean a real app bundles only referenced components. Importing
  `vuetify/components` wholesale is explicitly discouraged for production.
- **Reactive efficiency.** Subsystems use `shallowRef`/`toRef` and a single `watchEffect`
  per system (e.g. the display system recomputes all breakpoint booleans in one effect on
  resize) rather than many independent watchers.
- **Virtualization.** `VVirtualScroll`, `VDataTableVirtual`, and the `virtual` composable
  render only visible rows for large datasets.
- **Observer-based DOM integration.** Resize/intersection/mutation observers are attached
  with `{ passive: true }` listeners and torn down via `onScopeDispose`, avoiding leaks.
- **SSR hydration.** The display subsystem can be seeded with `ssr` dimensions and defers its
  first client update to prevent hydration mismatches.
- **CSS cascade layers** keep theme/utility specificity flat, avoiding `!important` arms
  races and reducing the cost of overriding styles.

> [!TIP]
> For the smallest production footprint: enable the Vite plugin's `autoImport`, use the SVG
> icon set (`vuetify/iconsets/mdi-svg`) instead of the full icon font, and import only the
> locales you actually ship from `vuetify/locale`.

---

## 14. Development & Operations

### Working on the framework

```bash
# Install (pnpm workspace)
pnpm install

# Run the component playground (packages/vuetify/dev)
pnpm --filter vuetify dev          # default http://localhost:8090

# SSR dev server
pnpm --filter vuetify dev:ssr

# Lint (type-check + eslint, concurrently)
pnpm --filter vuetify lint
pnpm --filter vuetify lint:fix

# Build the library
pnpm --filter vuetify build
```

Root-level orchestration scripts (`scripts/`):

| Script | Purpose |
|--------|---------|
| `dev.js` | Repo-wide dev launcher (`pnpm dev`). |
| `build.js` | Repo-wide build (`pnpm build`). |
| `post-install.js` | Husky + Playwright Chromium install on `prepare`. |
| `confirm-npm-tag.js` / `parse-npm-tag.js` | Release tag safety checks (`pnpm version`). |
| `post-release-merge.js` | Post-release branch merge. |
| `lint-commit-message.js` / `prepare-commit-message.js` | Conventional-commit enforcement. |
| `tdd-run.js` | Visual TDD runner. |

### Running the docs site

```bash
pnpm --filter vuetifyjs.com dev      # default host/port from packages/docs/.env (8095)
pnpm --filter vuetifyjs.com build
```

The docs site is a Vite SPA using `vue-router`, `vue-i18n`, Pinia, Algolia InstantSearch,
markdown-it (with many plugins), and a PWA service worker. Its `.env` configures Cosmic.js
CMS, EmailJS, and the search API.

### Release & nightly builds

Versioning uses `@lerna-lite`. The `master`, `dev`, and `next` branches publish nightly to
the `@vuetify/nightly` npm namespace at 1200 UTC for testing.

---

## 15. Contributing

- **Toolchain:** Node ≥ 24.11.1, `pnpm@10.26.1`. Run `pnpm install` at the repo root.
- **Commits:** Conventional Commits, enforced by commit-message hooks and
  `conventional-changelog-vuetify`.
- **Linting:** ESLint (with the local-rules plugin, `eslint-plugin-vuetify`, SonarJS, and
  Stylistic) plus `vue-tsc`/`tsgo` type-checking. `--max-warnings 0` — warnings fail CI.
- **Tests:** Add `*.spec.ts` (unit) and/or `*.spec.browser.ts` (browser) next to the code.
  Visual changes need Vizzly snapshots.
- **`all-checks`:** `pnpm all-checks` runs build + lint + tests across the workspace — the
  pre-PR gate.
- **Custom ESLint rules** live in `scripts/rules/` (e.g. import sorting) — respect them or
  CI fails.

> [!IMPORTANT]
> When adding or changing a component's props, the API generator picks them up from the
> built types automatically, but **prop descriptions** are authored separately per locale.
> The generator reports missing descriptions — fill them in so the docs site is complete.

---

## 16. Known Issues & Improvement Opportunities

Drawn from the project's own issue tracker and observable code behavior:

| Area | Observation | Suggested direction |
|------|-------------|---------------------|
| Theme transitions | `toggle()` can behave inconsistently once theme transitions are enabled (#22900). | Clear `_transitionOrigin` deterministically after each change; discourage the deprecated `theme.global.name.value` setter. |
| `VBtn` sizing | Numeric `size` does not scale font size (#16288). | Document the named-size scale; consider linking font scale to numeric size. |
| `VSelect` | `menu-props.maxHeight` dropped `%`/`vh` support (#16257); `multiple` + duplicate items can't deselect (#22878). | Restore unit support; key selection on a stable `item-value`. |
| `VDialog` | Percentage `max-width` not respected after a layout change (#22890). | Re-validate percentage width handling against the layout system. |
| Offline PWA icons | MDI **font** glyphs fail offline (#20227). | Recommend the SVG icon set for PWAs; precache font assets. |
| `VTreeview` | Keyboard support requested (#10796); a missing toggle `aria-label` was fixed (#22898). | Continue the a11y pass — keyboard navigation across nested groups. |
| `VCombobox` | Empty value for whitespace-only input (#14874). | Trim/normalize free-text input before commit. |

### Architectural maintainability notes

- The composable-injection design is clean but means **bugs often live in a shared
  composable**, not the reporting component. When triaging, check the relevant `use*`
  composable (e.g. a sizing bug → `composables/size.ts`) before the component file.
- `theme.ts` concentrates significant complexity (CSS generation, layers, variations,
  view-transitions, head integration). It is a good candidate for focused unit tests around
  `genCssVariables`, `genOnColors`, and `genVariations`.
- The monorepo's docs site carries its own large dependency set (CMS, search, analytics)
  that is unrelated to the framework; keep that boundary clear when auditing the published
  package's risk surface.

---

## 17. Quick Reference

### Subsystem → symbol → composable

| Subsystem | Injection key | Composable |
|-----------|---------------|------------|
| Theme | `vuetify:theme` | `useTheme()` |
| Display | `vuetify:display` | `useDisplay()` |
| Defaults | `vuetify:defaults` | `useDefaults()` |
| Locale | `vuetify:locale` | `useLocale()`, `useRtl()` |
| Icons | `vuetify:icons` | `useIcon()` |
| Date | `vuetify:date-adapter` | (date components) |
| GoTo | `vuetify:goto` | `useGoTo()` |

### Build targets

| Target | Output | Consumer use |
|--------|--------|--------------|
| `lib/` (Babel, per-file ESM) | `lib/framework.js` | npm install (tree-shakeable). |
| `dist/` (Rollup, bundled) | `vuetify.min.js`, `vuetify.esm.js` | CDN / `<script>`. |
| Types | `lib/**/*.d.ts`, bundled `dist/*.d.ts` | TypeScript consumers. |

### Common commands

```bash
pnpm install                        # bootstrap workspace
pnpm --filter vuetify dev           # component playground
pnpm --filter vuetify build         # build the library
pnpm --filter vuetify lint          # type-check + eslint
pnpm --filter vuetify test:unit     # unit tests (jsdom)
pnpm --filter vuetify test:browser  # browser tests (Playwright)
pnpm all-checks                     # build + lint + test (pre-PR gate)
```

### Inventory

| Asset | Count |
|-------|-------|
| Stable components | 104 |
| Labs components | 10 |
| Directives | 8 |
| Composables | ~63 |
| Icon sets | 15 |
| Locales | 43 |
| Blueprints | 3 (md1, md2, md3) |

---

_Generated June 2026 from vuetifyjs/vuetify at bbffe05. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
