@nonsuch/component-library
v0.11.0
Published
A Vue 3 component library built on Quasar with opinionated defaults and custom components.
Readme
@nonsuch/component-library
A Vue 3 component library built on top of Quasar, providing customized components with opinionated defaults.
Quasar components you haven't customized are used directly — this library only adds or overrides the ones with Nonsuch-specific styling and behavior. Tree-shaking is fully preserved.
Installation
# Install the library and its peer dependencies
pnpm add @nonsuch/component-library
pnpm add quasar @quasar/extras @quasar/vite-pluginQuick Start (Recommended)
Vite Configuration
import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue({ template: { transformAssetUrls } }), quasar()],
})App Entry
import { createApp } from 'vue'
import { Quasar } from 'quasar'
import { createNonsuch, createQuasarConfig } from '@nonsuch/component-library'
import '@nonsuch/component-library/tokens.css'
import 'quasar/src/css/index.sass'
const app = createApp(App)
app.use(Quasar, createQuasarConfig()) // Token-aligned Quasar brand colours
app.use(createNonsuch()) // Locale + library setup
app.mount('#app')That's it — components, tokens, locale (defaults to en-CA), and Quasar brand colours are all wired up.
Use Components
<script setup>
import { NsButton, NsInput, NsCard } from '@nonsuch/component-library'
</script>
<template>
<NsCard title="Welcome">
<NsInput v-model="name" label="Your name" />
<template #actions>
<NsButton label="Submit" />
</template>
</NsCard>
</template>Plugin Options
createNonsuch() accepts options for locale:
import { createNonsuch, nsLocaleFrCA } from '@nonsuch/component-library'
app.use(createNonsuch({ locale: nsLocaleFrCA }))createQuasarConfig() accepts brand colour overrides and extra Quasar config:
import { createQuasarConfig } from '@nonsuch/component-library'
app.use(
Quasar,
createQuasarConfig({
brand: { primary: '#1a73e8' },
plugins: { Notify: {} },
}),
)Dark Mode
import { useNsDarkMode } from '@nonsuch/component-library'
const { isDark, toggle, useSystem } = useNsDarkMode()The composable persists the user's choice to localStorage and syncs with prefers-color-scheme. Design tokens switch automatically via the dark class on <html>.
NsThemeProvider
For section-level locale overrides without a plugin:
<script setup>
import { NsThemeProvider, nsLocaleFrCA } from '@nonsuch/component-library'
</script>
<template>
<NsThemeProvider :locale="nsLocaleFrCA">
<!-- All Ns components here use French strings -->
</NsThemeProvider>
</template>Manual Setup (Advanced)
Fonts (Optional)
The library ships Fixel as the Nonsuch brand font with Roboto as a fallback. Three integration options:
Option 1: Global CSS (recommended) — makes Fixel the default for your entire app:
import '@nonsuch/component-library/fonts/global.css'This loads all @font-face declarations and sets Fixel Text on body and Fixel Display on headings.
Option 2: Font faces only — load the fonts without applying them globally, then use them where you choose:
import '@nonsuch/component-library/fonts.css'.my-element {
font-family: 'Fixel Text', 'Roboto', sans-serif;
}Option 3: Quasar Sass variables — integrates with Quasar's typography system:
// src/quasar-variables.sass
@use '@nonsuch/component-library/fonts/quasar-overrides' as *// vite.config.ts
quasar({ sassVariables: 'src/quasar-variables.sass' })Or use Quasar components directly — they aren't re-exported through this library, so you import them from quasar as normal:
import { QInput, QSelect } from 'quasar'Translations (i18n)
The library ships its own locale system — no dependency on vue-i18n. Components that render user-visible text accept optional string props with built-in defaults from the active locale.
Built-in locales: en-CA (default) and fr-CA.
Option 1: Use the defaults — components use English (Canada) strings out of the box with no setup:
<NsButton>Add to cart</NsButton>
<!-- internal labels like loading text already default to English -->Option 2: Switch locale globally — provide a locale pack at the app root:
import { createApp } from 'vue'
import { provideNsLocale, nsLocaleFrCA } from '@nonsuch/component-library'
const app = createApp(App)
// Inside your root component's setup():
provideNsLocale(nsLocaleFrCA)Option 3: Custom / partial locale — supply your own translations by implementing the NsLocaleMessages interface:
import type { NsLocaleMessages } from '@nonsuch/component-library'
import { nsLocaleEnCA, provideNsLocale } from '@nonsuch/component-library'
const myLocale: NsLocaleMessages = {
...nsLocaleEnCA,
product: {
...nsLocaleEnCA.product,
addToCart: 'Add to bag', // override just what you need
},
}
provideNsLocale(myLocale)Option 4: Override per-component — pass a string prop directly to bypass the locale:
<!-- This label is always "Ajouter" regardless of the active locale -->
<NsButton label="Ajouter" />The locale interface covers four sections: common, product, media, and validation. See the full type in NsLocaleMessages.
Design Tokens (Optional)
Import CSS custom properties for colours, typography, spacing, border-radius, shadows, and motion:
import '@nonsuch/component-library/tokens.css'All tokens use the --ns- prefix and support light/dark mode automatically. Current values are placeholders — token names are stable.
.my-card {
border-radius: var(--ns-radius-md);
box-shadow: var(--ns-shadow-sm);
padding: var(--ns-space-4);
}Dark mode activates via class="dark", data-theme="dark", Quasar's .q-dark, or prefers-color-scheme: dark.
Breakpoints
The library ships Quasar-aligned breakpoint values and media-query helpers:
import {
nsBreakpoints,
nsMediaUp,
nsMediaDown,
nsMediaOnly,
nsMediaBetween,
} from '@nonsuch/component-library'| Name | Min-width | Range | Note | | ---- | --------- | -------------- | ----- | | xs | 0 | 0 – 599 px | | | sm | 600 | 600 – 1023 px | | | md | 1024 | 1024 – 1439 px | | | lg | 1440 | 1440 – 1919 px | | | xl | 1920 | 1920 – 2559 px | | | xxl | 2560 | 2560 – 3839 px | 1440p | | xxxl | 3840 | 3840 px + | 4K |
Media-query helpers return matchMedia()-ready strings:
nsMediaUp('md') // '(min-width: 1024px)'
nsMediaDown('md') // '(max-width: 1023px)'
nsMediaOnly('md') // '(min-width: 1024px) and (max-width: 1439px)'
nsMediaBetween('sm', 'lg') // '(min-width: 600px) and (max-width: 1919px)'To customize in the future, spread and override:
const custom = { ...nsBreakpoints, lg: 1280 }Development
# Install dependencies
pnpm install
# Run Storybook (dev mode)
pnpm dev
# Run tests
pnpm test
pnpm test:watch
# Build the library
pnpm build
# Build Storybook for deployment
pnpm build:storybookProject Structure
src/
index.ts # Library entry — exports all public API
manifest.ts # Quasar → Ns component mapping for sync enforcement
plugin.ts # createNonsuch() Vue plugin
quasarConfig.ts # createQuasarConfig() helper
components/
NsAvatar/ # Avatar with size presets
NsBadge/ # Styled QBadge wrapper
NsBanner/ # Info/success/warning/error banners
NsBreadcrumbs/ # Breadcrumb navigation
NsBreadcrumbElement/ # Individual breadcrumb item
NsButton/ # Styled QBtn wrapper
NsButtonToggle/ # Toggle between button options
NsCard/ # Card with title/subtitle/actions slots
NsCardActions/ # Card action bar
NsCardSection/ # Card content section
NsCheckbox/ # Styled QCheckbox wrapper
NsChip/ # Tag/filter chip
NsDialog/ # Modal dialog with header/body/actions
NsDrawer/ # Side drawer (within NsLayout)
NsExpansionItem/ # Collapsible list item
NsFooter/ # App footer (within NsLayout)
NsForm/ # Form wrapper with validation
NsHeader/ # App header (within NsLayout)
NsIcon/ # Styled QIcon wrapper
NsImage/ # Styled QImg wrapper
NsInnerLoading/ # Overlay loading indicator
NsInput/ # Styled QInput wrapper
NsItem/ # List item
NsItemLabel/ # List item label
NsItemSection/ # List item section
NsLayout/ # App layout container
NsLinearProgress/ # Linear progress bar
NsList/ # List with separator defaults
NsMenu/ # Dropdown/context menu
NsPage/ # Page content area
NsPageContainer/ # Page container (within NsLayout)
NsPagination/ # Page navigation controls
NsSelect/ # Styled QSelect dropdown
NsSeparator/ # Horizontal/vertical separator
NsSkeleton/ # Loading skeleton with animation
NsSpace/ # Flex spacer
NsSpinner/ # Circular spinner
NsSpinnerDots/ # Dot-style spinner
NsTab/ # Tab item (within NsTabs)
NsTabPanel/ # Tab panel content
NsTabPanels/ # Tab panels container
NsTable/ # Data table
NsTableCell/ # Table cell
NsTabs/ # Tab navigation bar
NsThemeProvider/ # Renderless locale provider
NsTimeline/ # Timeline container
NsTimelineEntry/ # Timeline entry item
NsToggle/ # Styled QToggle switch
NsToolbar/ # Toolbar container
NsToolbarTitle/ # Toolbar title
NsTooltip/ # Tooltip with consistent delays
composables/
useNsLocale.ts # Locale injection/provision
useNsDarkMode.ts # Dark mode with persistence
useNsDefaults.ts # Default value helper
locale/ # en-CA, fr-CA string packs
breakpoints/ # Breakpoint values + media query helpers
tokens/ # Design token CSS + TS helpers
fonts/ # Fixel font files + CSSEach custom component lives in its own directory with co-located story and test files. The Ns prefix distinguishes library components from Quasar's Q prefix.
Adding a New Component
- Create
src/components/NsMyComponent/NsMyComponent.vue - Add a story:
NsMyComponent.stories.ts - Add tests:
NsMyComponent.test.ts - Add a re-export:
index.ts - Export from
src/index.ts
Architecture
- Quasar is a peer dependency — consumers install it normally and get full tree-shaking via
@quasar/vite-plugin - Custom components compose Quasar — they wrap or extend Quasar components with opinionated defaults
- Vite library mode — builds to ES modules with externalized
vueandquasar - TypeScript — strict mode with emitted
.d.tsdeclarations
Contributing
Want to add or improve components? Check out CONTRIBUTING.md for a friendly step-by-step guide — no deep Vue experience required!
License
MIT — see LICENSE
