TODOvue Settings (TvSettings)

January 21, 2026 · View on GitHub

TODOvue logo

TODOvue Settings (TvSettings)

A flexible Vue 3 component with no framework dependencies for creating sliding settings panels from any direction. Perfect for settings, side menus, or any content that needs to slide out from screen edges. Compatible with SPA applications and server-side rendering (SSR) environments like Nuxt 3.

npm npm downloads npm total downloads License Release Date Bundle Size Node Version Last Commit Stars

Demo: https://ui.todovue.blog/settings

Table of Contents

Features

  • Sliding panels from 4 directions: top, right, bottom, left
  • Bidirectional v-model control (controlled mode) or internal state
  • Automatic close on outside click (configurable)
  • Close with Escape key
  • Customizable slots (header and main content)
  • Built-in gear SVG icon
  • Disabled state
  • Smooth transitions
  • Compatible with SPA and SSR (Nuxt 3)
  • Tree-shake friendly (Vue marked as external in build)
  • TypeScript declarations included

Installation

Using npm:

npm install @todovue/tv-settings

Using yarn:

yarn add @todovue/tv-settings

Using pnpm:

pnpm add @todovue/tv-settings

Quick Start (SPA)

Global registration (main.js / main.ts):

import { createApp } from 'vue'
import App from './App.vue'
import TvSettings from '@todovue/tv-settings'

createApp(App)
  .use(TvSettings) // enables <TvSettings /> globally
  .mount('#app')

Local import inside a component:

<script setup>
import { TvSettings } from '@todovue/tv-settings'
import '@todovue/tv-settings/style.css'
</script>

<template>
  <TvSettings direction="right">
    <template #header>
      <h2>Settings</h2>
    </template>
    <template #default="{ close }">
      <p>Custom content</p>
      <button @click="close">Close</button>
    </template>
  </TvSettings>
</template>

Usage in Nuxt 4 / SSR

// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@todovue/tv-button/nuxt'
  ]
})

Use anywhere:

<TvSettings direction="left">
  <template #default="{ direction }">
    <p>Panel from {{ direction }}</p>
  </template>
</TvSettings>

Optional direct import (without plugin):

<script setup>
import { TvSettings } from '@todovue/tv-settings'
import '@todovue/tv-settings/style.css'
</script>

Component Registration Options

ApproachWhen to use
Global via app.use(TvSettings)Multiple uses across the app / design system
Named local import { TvSettings }Isolated contexts / code-split
Default direct import import TvSettings from '@todovue/tv-settings'Single use or manual registration

Props

PropTypeDefaultDescription
modelValueBooleanundefinedBidirectional v-model control. If undefined, uses internal state.
directionString'right'Direction from which the panel slides: 'top', 'right', 'bottom', 'left'
disabledBooleanfalseDisables the open button.
closeOnOutsideBooleantrueIf true, the panel closes when clicking outside.
labelString'Open settings'ARIA label for the gear button (accessibility).
titleStringnullOptional title to display in the header.

Events

Event (kebab)Emit (camel)Description
update:modelValueupdate:modelValueEmitted when open/closed state changes.
openopenEmitted when the panel opens.
closecloseEmitted when the panel closes.

Usage:

<TvSettings 
  v-model="isOpen"
  @open="handleOpen" 
  @close="handleClose" 
/>

Slots

SlotBindingsDescription
trigger{ isOpen, toggle, open, close }Replace the default gear button.
headerPanel header content (optional).
default{ direction, close, open }Main panel content with access to methods.

Slot Examples

Header slot:

<TvSettings>
  <template #header>
    <h2>My Settings</h2>
  </template>
</TvSettings>

Default slot with methods:

<TvSettings>
  <template #default="{ direction, close }">
    <p>Panel from: {{ direction }}</p>
    <button @click="close">Close</button>
  </template>
</TvSettings>

Trigger slot:

<TvSettings>
  <template #trigger="{ toggle }">
    <button @click="toggle">My Custom Button</button>
  </template>
  <template #default>
    <p>Content</p>
  </template>
</TvSettings>

Directions

The component supports 4 directions:

  • top - Panel slides from top
  • right - Panel slides from right (default)
  • bottom - Panel slides from bottom
  • left - Panel slides from left

Examples:

<TvSettings direction="top" />
<TvSettings direction="right" />
<TvSettings direction="bottom" />
<TvSettings direction="left" />

If an invalid direction is provided, the component will show a console warning and use 'right' as default.

State Control

Uncontrolled Mode (Internal State)

By default, the component manages its own state:

<TvSettings direction="left">
  <p>Panel content</p>
</TvSettings>

Controlled Mode (v-model)

Control state externally:

<script setup>
import { ref } from 'vue'
const isOpen = ref(false)
</script>

<template>
  <button @click="isOpen = !isOpen">Toggle Settings</button>
  <TvSettings v-model="isOpen" direction="right">
    <p>Controlled panel</p>
  </TvSettings>
</template>

Disable Close on Outside Click

<TvSettings :close-on-outside="false" direction="left">
  <template #default="{ close }">
    <p>Only closes with button or ESC</p>
    <button @click="close">Close</button>
  </template>
</TvSettings>

Customization (Styles / Themes)

The component uses CSS with BEM classes. You can override styles:

/* Customize gear button */
.tv-setting__gear {
  background-color: #your-color;
  color: #your-icon-color;
}

/* Customize panel */
.tv-setting__panel {
  background-color: #your-panel-bg;
  box-shadow: 0 4px 20px rgba(0,0,0,0.3);
}

/* Customize by direction */
.tv-setting--top .tv-setting__panel {
  /* specific styles for top panel */
}

Inline styles:

<TvSettings 
  direction="right"
  style="--panel-bg: #f0f0f0; --panel-width: 350px;"
>
  <p>Custom panel</p>
</TvSettings>

Accessibility

  • The gear button includes aria-label, aria-pressed and aria-expanded.
  • You can customize the aria-label text with the label prop.
  • The panel has role="dialog".
  • Supports close with Escape key.
  • Proper focus management for screen readers.

Example:

<TvSettings label="Open settings panel" direction="right">
  <p>Accessible content</p>
</TvSettings>

SSR Notes

  • No direct DOM access (window / document) in source code → SSR-safe.
  • Styles are automatically applied when importing the library.
  • Make sure to import @todovue/tv-settings/style.css in an SSR-compatible entry point (plugin or layout).
  • Event listeners (outside click, keydown) are registered in onMounted to avoid SSR errors.

Development

git clone https://github.com/TODOvue/tv-settings.git
cd tv-settings
npm install
npm run dev     # run demo playground
npm run build   # build library

Local demo is served from Vite using index.html + examples in src/demo.

Contributing

PRs and issues are welcome. See CONTRIBUTING.md and CODE_OF_CONDUCT.md.

License

MIT © TODOvue

Attributions

Developed for the TODOvue component ecosystem