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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@fastkit/vue-color-scheme

v0.17.0

Published

A library for using Type-safe "color" schemas in Vue applications.

Readme

@fastkit/vue-color-scheme

🌐 English | 日本語

A library for using type-safe color schemes in Vue.js applications. Integrates @fastkit/color-scheme with Vue 3 Composition API, providing dynamic theme switching, CSS Variables integration, and type-safe color access.

Features

  • Full Vue 3 Integration: Supports both Composition API and Options API
  • Type Safety: Complete type safety with TypeScript
  • Dynamic Theme Switching: Real-time light/dark theme switching
  • CSS Variables Integration: Automatic CSS variable generation and binding
  • Composables: Convenient composables like useColorScheme, useColorClasses
  • Props Integration: Standardized prop definitions with colorSchemeProps
  • HTML Class Management: Automatic HTML binding of theme classes
  • Head Management: Meta information management through @unhead/vue integration
  • Plugin System: Easy setup at Vue application level
  • SSR Support: Full server-side rendering support

Installation

npm install @fastkit/vue-color-scheme
# or
pnpm add @fastkit/vue-color-scheme

# Dependencies
npm install @fastkit/color-scheme vue @unhead/vue

Basic Usage

Plugin Configuration

// main.ts
import { createApp } from 'vue';
import { createHead } from '@unhead/vue';
import { VueColorSchemeService } from '@fastkit/vue-color-scheme';
import colorScheme from './color-scheme'; // Color scheme definition

const app = createApp(App);

// Head plugin
const head = createHead();
app.use(head);

// Color scheme service
const colorSchemeService = new VueColorSchemeService(colorScheme);
colorSchemeService.provide(app);

app.mount('#app');

Color Scheme Definition

// color-scheme.ts
import { createSimpleColorScheme } from '@fastkit/color-scheme-gen';

export default createSimpleColorScheme({
  primary: '#1976d2',
  secondary: '#424242',
  success: '#4caf50',
  warning: '#ff9800',
  error: '#f44336',
  background: '#ffffff',
  foundation: '#f5f5f5'
});

Using in Vue Components

<template>
  <div :class="themeClass">
    <!-- Primary color button -->
    <button :class="primaryClasses">
      Primary Button
    </button>

    <!-- Secondary color outline button -->
    <button :class="secondaryClasses">
      Secondary Button
    </button>

    <!-- Theme toggle button -->
    <button @click="toggleTheme">
      Switch to {{ isDark ? 'Light' : 'Dark' }} Theme
    </button>
  </div>
</template>

<script setup lang="ts">
import {
  useColorScheme,
  useColorClasses,
  useThemeClass
} from '@fastkit/vue-color-scheme';

// Color scheme service
const colorScheme = useColorScheme();

// Theme class management
const { themeClass, currentTheme, toggleTheme, isDark } = useThemeClass({});

// Color class generation
const primaryClasses = useColorClasses({ color: 'primary', variant: 'contained' });
const secondaryClasses = useColorClasses({ color: 'secondary', variant: 'outlined' });
</script>

Composables

useColorScheme

Access to the color scheme service:

import { useColorScheme } from '@fastkit/vue-color-scheme';

const colorSchemeService = useColorScheme();

// Service information
console.log(colorSchemeService.defaultTheme);    // 'light'
console.log(colorSchemeService.themeNames);      // ['light', 'dark']
console.log(colorSchemeService.paletteNames);    // ['primary', 'secondary', ...]
console.log(colorSchemeService.scopeNames);      // ['primary', 'secondary', ...]

// Current theme
console.log(colorSchemeService.rootTheme);       // 'light' | 'dark'
colorSchemeService.rootTheme = 'dark';           // Change theme

useThemeClass

Theme class management:

import { useThemeClass } from '@fastkit/vue-color-scheme';

// Basic usage
const themeResult = useThemeClass({});
console.log(themeResult.value); // { value: 'light', className: 'light-theme' }

// Specify a specific theme
const themeResult = useThemeClass({ theme: 'dark' });
console.log(themeResult.value); // { value: 'dark', className: 'dark-theme' }

// Reactive theme
const theme = ref('light');
const themeResult = useThemeClass({ theme });

// Use root theme as default
const themeResult = useThemeClass({}, true);

useColorClasses

Comprehensive color class management:

import { useColorClasses } from '@fastkit/vue-color-scheme';

const colorClassesResult = useColorClasses({
  theme: 'light',
  color: 'primary',
  variant: 'contained',
  textColor: 'white',
  borderColor: 'primary'
});

// Result structure
console.log(colorClassesResult.theme.value);        // { value: 'light', className: 'light-theme' }
console.log(colorClassesResult.color.value);        // { value: 'primary', className: 'primary-scope' }
console.log(colorClassesResult.variant.value);      // { value: 'contained', className: 'contained' }
console.log(colorClassesResult.textColor.value);    // { value: 'white', className: 'white-text' }
console.log(colorClassesResult.borderColor.value);  // { value: 'primary', className: 'primary-border' }

// Get all classes as an array
console.log(colorClassesResult.colorClasses.value);
// ['light-theme', 'primary-scope', 'contained', 'white-text', 'primary-border']

useScopeColorClass

Dedicated scope color class:

import { useScopeColorClass } from '@fastkit/vue-color-scheme';

const scopeResult = useScopeColorClass({ color: 'primary' });
console.log(scopeResult.value); // { value: 'primary', className: 'primary-scope' }

// Dynamic color
const color = ref('secondary');
const scopeResult = useScopeColorClass({ color });
// Class name automatically changes when color changes

useTextColorClass

Dedicated text color class:

import { useTextColorClass } from '@fastkit/vue-color-scheme';

const textResult = useTextColorClass({ textColor: 'primary' });
console.log(textResult.value); // { value: 'primary', className: 'primary-text' }

// Function-based dynamic color
const textResult = useTextColorClass({
  textColor: () => isDark.value ? 'white' : 'black'
});

useBorderColorClass

Dedicated border color class:

import { useBorderColorClass } from '@fastkit/vue-color-scheme';

const borderResult = useBorderColorClass({ borderColor: 'primary' });
console.log(borderResult.value); // { value: 'primary', className: 'primary-border' }

useColorVariantClasses

Dedicated variant class:

import { useColorVariantClasses } from '@fastkit/vue-color-scheme';

const variantResult = useColorVariantClasses({ variant: 'outlined' });
console.log(variantResult.value); // { value: 'outlined', className: 'outlined' }

useInjectTheme

Automatic theme application to HTML class:

import { useInjectTheme } from '@fastkit/vue-color-scheme';

// When this composable is called,
// theme classes are automatically added to the HTML class attribute
const colorSchemeService = useInjectTheme();

// HTML example: <html class="light-theme">
// Class automatically changes when theme changes

Props Integration

colorSchemeProps

Standardized color scheme props definition:

import { colorSchemeProps } from '@fastkit/vue-color-scheme';

// Default prop names
const props = colorSchemeProps();
// Generated props:
// {
//   variant: String,
//   theme: String,
//   color: String,
//   textColor: String,
//   borderColor: String
// }

// Custom prop names
const customProps = colorSchemeProps({
  theme: 'appTheme',
  color: 'brandColor',
  textColor: 'fontColor',
  borderColor: 'edgeColor'
});
// Generated props:
// {
//   variant: String,
//   appTheme: String,
//   brandColor: String,
//   fontColor: String,
//   edgeColor: String
// }

// Usage example
export default defineComponent({
  props: {
    ...colorSchemeProps(),
    title: String,
    size: String
  },
  setup(props) {
    const colorClasses = useColorClasses(props);

    return {
      colorClasses
    };
  }
});

Advanced Usage Examples

Component Library Integration

<!-- Button.vue -->
<template>
  <button
    :class="buttonClasses"
    :disabled="disabled"
    @click="$emit('click', $event)"
  >
    <slot />
  </button>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { colorSchemeProps, useColorClasses } from '@fastkit/vue-color-scheme';

interface Props {
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
}

const props = defineProps({
  ...colorSchemeProps(),
  ...withDefaults(defineProps<Props>(), {
    size: 'md',
    disabled: false
  })
});

defineEmits<{
  click: [event: PointerEvent];
}>();

// Color class generation
const colorClasses = useColorClasses(props);

// Final class name calculation
const buttonClasses = computed(() => [
  'button',
  `button--${props.size}`,
  ...colorClasses.colorClasses.value,
  {
    'button--disabled': props.disabled
  }
]);
</script>

<style scoped>
.button {
  /* Base styles */
  padding: var(--spacing-md);
  border-radius: var(--border-radius);
  font-weight: 500;
  transition: all 0.2s ease;
  cursor: pointer;
}

.button--sm { padding: var(--spacing-sm); }
.button--md { padding: var(--spacing-md); }
.button--lg { padding: var(--spacing-lg); }

.button--disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Using color scheme CSS variables */
.button.primary-scope.contained {
  background: var(--main-color);
  color: var(--text-color);
  border: 1px solid var(--main-color);
}

.button.primary-scope.contained:hover {
  background: var(--deep-color);
}

.button.primary-scope.outlined {
  background: transparent;
  color: var(--main-color);
  border: 1px solid var(--outlineBorder-color);
}
</style>

Theme Toggle Component

<!-- ThemeSwitcher.vue -->
<template>
  <div class="theme-switcher">
    <button
      v-for="themeName in availableThemes"
      :key="themeName"
      :class="themeButtonClass(themeName)"
      @click="setTheme(themeName)"
    >
      {{ getThemeDisplayName(themeName) }}
    </button>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { useColorScheme } from '@fastkit/vue-color-scheme';

const colorSchemeService = useColorScheme();

// Available themes
const availableThemes = computed(() => colorSchemeService.themeNames);

// Current theme
const currentTheme = computed(() => colorSchemeService.rootTheme);

// Theme setting
const setTheme = (themeName: string) => {
  colorSchemeService.rootTheme = themeName;
};

// Theme button class
const themeButtonClass = (themeName: string) => [
  'theme-button',
  {
    'theme-button--active': currentTheme.value === themeName
  }
];

// Theme display name
const getThemeDisplayName = (themeName: string) => {
  const displayNames: Record<string, string> = {
    light: 'Light',
    dark: 'Dark',
    auto: 'Auto'
  };
  return displayNames[themeName] || themeName;
};
</script>

<style scoped>
.theme-switcher {
  display: flex;
  gap: var(--spacing-sm);
}

.theme-button {
  padding: var(--spacing-sm) var(--spacing-md);
  border: 1px solid var(--border-color);
  background: var(--background-color);
  color: var(--text-color);
  border-radius: var(--border-radius);
  cursor: pointer;
  transition: all 0.2s ease;
}

.theme-button:hover {
  background: var(--light-color);
}

.theme-button--active {
  background: var(--primary-color);
  color: var(--primary-text-color);
  border-color: var(--primary-color);
}
</style>

System Settings Integration

<!-- AutoThemeManager.vue -->
<template>
  <div class="auto-theme-manager">
    <label>
      <input
        type="checkbox"
        :checked="followSystem"
        @change="toggleSystemFollow"
      >
      Follow system settings
    </label>

    <div v-if="!followSystem" class="manual-controls">
      <ThemeSwitcher />
    </div>

    <div class="current-info">
      Current theme: {{ currentTheme }}
      <span v-if="followSystem">(System: {{ systemTheme }})</span>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useColorScheme } from '@fastkit/vue-color-scheme';

const colorSchemeService = useColorScheme();
const followSystem = ref(true);
const systemTheme = ref<'light' | 'dark'>('light');

// Current theme
const currentTheme = computed(() => colorSchemeService.rootTheme);

// System settings monitoring
let mediaQuery: MediaQueryList | null = null;

const updateSystemTheme = () => {
  if (mediaQuery) {
    systemTheme.value = mediaQuery.matches ? 'dark' : 'light';
    if (followSystem.value) {
      colorSchemeService.rootTheme = systemTheme.value;
    }
  }
};

const toggleSystemFollow = (event: Event) => {
  const target = event.target as HTMLInputElement;
  followSystem.value = target.checked;

  if (followSystem.value) {
    // When following system settings, apply current system theme
    colorSchemeService.rootTheme = systemTheme.value;
  }
};

onMounted(() => {
  // Monitor system dark mode settings
  mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
  updateSystemTheme();

  mediaQuery.addEventListener('change', updateSystemTheme);
});

onUnmounted(() => {
  if (mediaQuery) {
    mediaQuery.removeEventListener('change', updateSystemTheme);
  }
});
</script>

Dynamic Color Generation

<!-- DynamicColorGenerator.vue -->
<template>
  <div class="color-generator">
    <div class="controls">
      <label>
        Base Color:
        <input
          type="color"
          v-model="baseColor"
          @input="generateColors"
        >
      </label>

      <label>
        Color Mode:
        <select v-model="colorMode" @change="generateColors">
          <option value="monochromatic">Monochromatic</option>
          <option value="analogous">Analogous</option>
          <option value="complementary">Complementary</option>
          <option value="triadic">Triadic</option>
        </select>
      </label>
    </div>

    <div class="color-preview">
      <div
        v-for="(color, index) in generatedColors"
        :key="index"
        class="color-swatch"
        :style="{ backgroundColor: color }"
      >
        {{ color }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useColorScheme } from '@fastkit/vue-color-scheme';

const colorSchemeService = useColorScheme();
const baseColor = ref('#1976d2');
const colorMode = ref('monochromatic');
const generatedColors = ref<string[]>([]);

const generateColors = () => {
  // Color generation logic (use @fastkit/color in actual implementation)
  const colors: string[] = [];

  switch (colorMode.value) {
    case 'monochromatic':
      // Monochromatic palette with different brightness
      colors.push(
        baseColor.value,
        adjustBrightness(baseColor.value, 0.2),
        adjustBrightness(baseColor.value, -0.2)
      );
      break;

    case 'analogous':
      // Analogous color palette
      colors.push(
        baseColor.value,
        rotateHue(baseColor.value, 30),
        rotateHue(baseColor.value, -30)
      );
      break;

    case 'complementary':
      // Complementary color palette
      colors.push(
        baseColor.value,
        rotateHue(baseColor.value, 180)
      );
      break;

    case 'triadic':
      // Triadic color palette
      colors.push(
        baseColor.value,
        rotateHue(baseColor.value, 120),
        rotateHue(baseColor.value, 240)
      );
      break;
  }

  generatedColors.value = colors;
};

// Color manipulation helper functions (simplified version)
const adjustBrightness = (color: string, amount: number): string => {
  // Use @fastkit/color Color.lighten/darken methods in implementation
  return color;
};

const rotateHue = (color: string, degrees: number): string => {
  // Use @fastkit/color Color.rotate method in implementation
  return color;
};

onMounted(() => {
  generateColors();
});
</script>

<style scoped>
.color-generator {
  padding: var(--spacing-lg);
}

.controls {
  display: flex;
  gap: var(--spacing-md);
  margin-bottom: var(--spacing-lg);
}

.controls label {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
}

.color-preview {
  display: flex;
  gap: var(--spacing-sm);
}

.color-swatch {
  width: 120px;
  height: 80px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-weight: bold;
  text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
  border-radius: var(--border-radius);
}
</style>

VueColorSchemeService

Detailed usage of service class:

import { VueColorSchemeService } from '@fastkit/vue-color-scheme';
import colorScheme from './color-scheme';

// Service creation
const service = new VueColorSchemeService(colorScheme);

// Property access
console.log(service.scheme);          // ColorScheme instance
console.log(service.defaultTheme);    // Default theme name
console.log(service.themeNames);      // All theme names
console.log(service.paletteNames);    // All palette names
console.log(service.scopeNames);      // All scope names

// Theme management
console.log(service.rootTheme);       // Current theme
service.rootTheme = 'dark';          // Theme change

// Provide to Vue app
service.provide(app);

SSR Support

Usage in server-side rendering:

// server.ts (Node.js server)
import { createSSRApp } from 'vue';
import { renderToString } from 'vue/server-renderer';
import { createHead, renderHeadToString } from '@unhead/vue';
import { VueColorSchemeService } from '@fastkit/vue-color-scheme';

export async function render(url: string, theme = 'light') {
  const app = createSSRApp(App);

  // Head setup
  const head = createHead();
  app.use(head);

  // Color scheme setup
  const colorSchemeService = new VueColorSchemeService(colorScheme);
  colorSchemeService.rootTheme = theme; // Set theme on server side
  colorSchemeService.provide(app);

  // Rendering
  const appHtml = await renderToString(app);
  const { headTags, htmlAttrs, bodyAttrs } = await renderHeadToString(head);

  return {
    html: `
      <!DOCTYPE html>
      <html${htmlAttrs}>
        <head>
          ${headTags}
        </head>
        <body${bodyAttrs}>
          <div id="app">${appHtml}</div>
        </body>
      </html>
    `,
  };
}

TypeScript Type Extensions

Extending type information:

// types/vue-color-scheme.d.ts
declare module '@fastkit/vue-color-scheme' {
  interface ColorSchemeHooksProps {
    // Add custom props
    brand?: string;
    accent?: string;
  }
}

// Usage example
const customColorClasses = useColorClasses({
  color: 'primary',
  brand: 'corporate',  // Custom prop
  accent: 'highlight'  // Custom prop
});

Testing and Debugging

Unit Tests

import { describe, test, expect, beforeEach } from 'vitest';
import { mount } from '@vue/test-utils';
import { VueColorSchemeService } from '@fastkit/vue-color-scheme';
import { createSimpleColorScheme } from '@fastkit/color-scheme-gen';

describe('VueColorScheme', () => {
  let colorScheme: any;
  let service: VueColorSchemeService;

  beforeEach(() => {
    colorScheme = createSimpleColorScheme({
      primary: '#1976d2',
      secondary: '#424242'
    });
    service = new VueColorSchemeService(colorScheme);
  });

  test('service creation', () => {
    expect(service.defaultTheme).toBe('light');
    expect(service.themeNames).toContain('light');
    expect(service.themeNames).toContain('dark');
  });

  test('theme switching', () => {
    expect(service.rootTheme).toBe('light');
    service.rootTheme = 'dark';
    expect(service.rootTheme).toBe('dark');
  });

  test('component integration', () => {
    const TestComponent = {
      template: '<div :class="colorClasses.colorClasses.value"></div>',
      setup() {
        const colorClasses = useColorClasses({
          color: 'primary',
          variant: 'contained'
        });
        return { colorClasses };
      }
    };

    const wrapper = mount(TestComponent, {
      global: {
        provide: {
          [VueColorSchemeServiceInjectionKey]: service
        }
      }
    });

    expect(wrapper.classes()).toContain('primary-scope');
    expect(wrapper.classes()).toContain('contained');
  });
});

Dependencies

{
  "dependencies": {
    "@fastkit/color-scheme": "Color scheme foundation library",
    "@fastkit/tiny-logger": "Lightweight logging functionality",
    "@fastkit/vue-utils": "Vue.js utilities"
  },
  "peerDependencies": {
    "vue": "^3.5.0",
    "@unhead/vue": "^1.8.0"
  }
}

Documentation

https://dadajam4.github.io/fastkit/vue-color-scheme/

License

MIT