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

good-old-ui

v0.0.32

Published

A retro Windows 3.11 / MS-DOS UI component library for Vue 3

Readme

good-old-ui

A retro UI component library for Vue 3. Ships two built-in themes:

| Theme | Look | |---|---| | Windows 3.11 (default) | Chunky 3-D bevelled borders, teal desktop, grey panels | | MS-DOS | Flat CGA-blue panels, monospace font, cyan selection |

Switch themes at runtime with the useTheme() composable — no page reload required.


Components

| Component | Description | |---|---| | GouButton | Raised 3-D button with pressed state | | GouInput | Deep-inset single-line text field | | GouTextarea | Deep-inset multi-line text area with retro scrollbar | | GouCheckbox | Square checkbox with ✓ mark | | GouRadio | Circular radio button | | GouSelect | Combo-box dropdown with keyboard nav | | GouProgressBar | Segmented progress bar (+ indeterminate mode) | | GouSlider | Drag-based horizontal range slider | | GouTabs + GouTabPanel | Tabbed panel container | | GouMenuBar | Application menu bar with dropdowns | | GouDialog | Modal dialog with stipple overlay | | GouTooltip | Hover tooltip wrapper | | GouLoader | Block or spinner loader with optional full-screen overlay | | GouDesktop | Full-screen desktop workspace container | | GouWindow | Draggable, resizable, minimisable window |


Quick Start

1. Install

npm install good-old-ui

2. Load styles globally

In your app entry (main.ts) before calling createApp — order matters:

// 1. Structural resets — always required
import 'good-old-ui/dist/css/good-old-base.css'

// 2. Windows 3.11 theme — required as the default
import 'good-old-ui/dist/css/good-old-theme-win311.css'

// 3. MS-DOS theme — import if you want runtime theme switching
import 'good-old-ui/dist/css/good-old-theme-msdos.css'

// 4. Bundled component scoped styles
import 'good-old-ui/dist/css/good-old-ui.css'

Note: The theme CSS files (items 1–3) define CSS custom property tokens (--gou-*). The bundled component CSS (item 4) contains only scoped styles that consume those tokens. All four files are separate by design so you can opt out of any theme you don't need.

Prevent theme flash on page reload (recommended)

When users can persist their theme preference, add this inline script to index.html before any <link> or <script> tags so the browser applies the correct data-gou-theme attribute on the very first paint:

<script>
  (function () {
    var valid = ['win311', 'msdos'];
    var stored = localStorage.getItem('gou-theme');
    document.documentElement.setAttribute(
      'data-gou-theme',
      valid.indexOf(stored) !== -1 ? stored : 'win311'
    );
  })();
</script>

3. Wrap your app in gou-scope

The library's CSS resets and scrollbar styles are scoped to .gou-scope to avoid bleeding into the host page:

<template>
  <div class="gou-scope" style="width: 100vw; height: 100vh;">
    <GouDesktop>
      <!-- windows live here -->
    </GouDesktop>
  </div>
</template>

4. Import and use components

<script setup lang="ts">
import {
  GouDesktop, GouWindow,
  GouButton, GouInput, GouCheckbox, GouRadio,
  GouSelect, GouProgressBar, GouSlider,
  GouTabs, GouTabPanel,
  GouMenuBar, GouDialog, GouTooltip,
  useTheme,
} from 'good-old-ui'
import { ref } from 'vue'

const { currentTheme, themes, setTheme, toggleTheme } = useTheme()

const name     = ref('')
const agreed   = ref(false)
const progress = ref(30)
const tab      = ref('tab1')
const dialog   = ref(false)

const menus = [
  {
    label: 'File',
    items: [
      { label: 'New',  action: () => console.log('New') },
      { separator: true },
      { label: 'Exit', action: () => console.log('Exit') },
    ],
  },
  { label: 'Help', items: [{ label: 'About', action: () => (dialog.value = true) }] },
]
</script>

<template>
  <div class="gou-scope" style="width:100vw;height:100vh;">
    <GouDesktop>
      <GouWindow title="Demo" window-id="demo"
                 :initial-x="40" :initial-y="40"
                 :initial-width="480" :initial-height="380">

        <GouMenuBar :menus="menus" />

        <div style="padding:8px;display:flex;flex-direction:column;gap:8px;">
          <GouInput v-model="name" placeholder="Your name…" />
          <GouCheckbox v-model="agreed" label="I accept the terms" />
          <GouProgressBar :value="progress" :label="`${progress}%`" />
          <GouSlider v-model="progress" :show-value="true" />

          <GouTabs v-model="tab"
            :tabs="[{ id:'tab1', label:'General' }, { id:'tab2', label:'Advanced' }]">
            <GouTabPanel id="tab1">General settings.</GouTabPanel>
            <GouTabPanel id="tab2">Advanced settings.</GouTabPanel>
          </GouTabs>

          <!-- Theme switcher -->
          <div style="display:flex;gap:6px;">
            <GouButton
              v-for="t in themes" :key="t.id" :label="t.label"
              @click="setTheme(t.id)"
            />
          </div>
        </div>
      </GouWindow>

      <GouDialog v-model="dialog" title="About good-old-ui" :width="300">
        <p>good-old-ui — a retro Vue 3 component library.</p>
        <template #footer>
          <GouButton label="OK" @click="dialog = false" />
        </template>
      </GouDialog>
    </GouDesktop>
  </div>
</template>

Themes & Runtime Switching

useTheme() composable

import { useTheme } from 'good-old-ui'

const { currentTheme, themes, setTheme, toggleTheme } = useTheme()

| Return | Type | Description | |---|---|---| | currentTheme | Readonly<Ref<GouTheme>> | Reactive current theme id | | themes | readonly ThemeDescriptor[] | All available themes with labels | | setTheme(id) | (id: GouTheme) => void | Switch immediately + persist to localStorage | | toggleTheme() | () => void | Cycle through all themes in order |

setTheme('msdos')   // switch to MS-DOS
setTheme('win311')  // switch back to Windows 3.11
toggleTheme()       // win311 → msdos → win311 → …

How it works

setTheme() writes data-gou-theme="<id>" to <html>. The MS-DOS theme CSS uses [data-gou-theme="msdos"] selectors (higher specificity than :root) to override the Win311 CSS custom properties. One attribute on <html> instantly re-tokens every component — no page reload, no per-component class toggling.

Custom themes

Override any --gou-* token under your own attribute:

[data-gou-theme="amber"] {
  --gou-color-bg:           #1a0e00;
  --gou-color-fg:           #ffaa00;
  --gou-color-desktop:      #000000;
  --gou-font-family:        "Courier New", monospace;
  --gou-text-shadow-emboss: none;
  /* … */
}

Then call setTheme('amber' as GouTheme) (or extend the GouTheme union type).


CSS Architecture

Published file layout

After npm run build, the dist/ directory contains:

dist/
├── js/
│   ├── good-old-ui.js        # ESM bundle (import)
│   └── good-old-ui.umd.cjs   # UMD bundle (require / <script>)
├── css/
│   ├── good-old-ui.css       # Bundled component scoped styles
│   ├── good-old-base.css     # Structural resets (layout, focus, scrollbar hooks)
│   ├── good-old-theme-win311.css  # Windows 3.11 tokens + shadows
│   └── good-old-theme-msdos.css  # MS-DOS tokens + flat borders
└── index.d.ts                # TypeScript declarations

What each CSS file contains

| File | Contains | Import | |---|---|---| | good-old-base.css | Box-sizing, font, focus, scrollbar structure | Always required | | good-old-theme-win311.css | Win 3.11 --gou-* tokens, 3-D shadow vars | Default theme | | good-old-theme-msdos.css | MS-DOS --gou-* token overrides, flat borders | For switching | | good-old-ui.css | Scoped component styles (consume --gou-* tokens) | Always required |

good-old-ui.css (the bundled output) does not include the token files. Import the theme CSS files separately.

CSS custom property tokens

| Token | Purpose | |---|---| | --gou-color-bg | Window / panel background | | --gou-color-fg | Default text colour | | --gou-color-desktop | Desktop background | | --gou-color-active-title | Active window title bar background | | --gou-color-highlight | Selected / focused item background | | --gou-color-input-bg | Text input background | | --gou-color-tooltip-bg | Tooltip background | | --gou-color-tooltip-fg | Tooltip text colour | | --gou-font-family | Base font stack | | --gou-font-size | Base font size | | --gou-shadow-raised | 3-D raised surface shadow | | --gou-shadow-sunken | 3-D sunken / pressed shadow | | --gou-shadow-deep-inset | Deep inset (text inputs, checkboxes) | | --gou-shadow-tab | Tab strip 3-D border | | --gou-shadow-footer-inset | Dialog footer separator | | --gou-shadow-separator | Menu separator highlight | | --gou-text-shadow-emboss | Disabled text drop-shadow (none in flat themes) | | --gou-color-border | Generic 1-px border colour | | --gou-color-shadow-mid | Mid-tone for separators and borders |

3-D border approach (Win311)

Win311's bevelled look uses four-layer box-shadow — no border property:

inset  1px  1px 0 #ffffff   ← inner top-left highlight
inset -1px -1px 0 #808080   ← inner bottom-right shadow
       1px  1px 0 #000000   ← outer bottom-right black edge
      -1px -1px 0 #dfdfdf   ← outer top-left light edge

Invert the inner layers for the pressed state. The MS-DOS theme replaces all of this with 0 0 0 1px <border-color> (single flat outline).


Development

Setup

git clone https://github.com/yourname/good-old-ui.git
cd good-old-ui
npm install

Dev server

npm run dev

Opens the interactive demo at http://localhost:5173. The Display Properties window (bottom-left, below the Component Gallery) contains the theme switcher.

Type-check

npm run type-check

Tests

npm test            # run once
npm run test:watch  # watch mode

Building for Production

npm run build

Outputs to dist/ as shown above. The build:

  1. Type-checks (vue-tsc --noEmit)
  2. Bundles components into dist/js/ and scoped CSS into dist/css/
  3. Copies the three theme CSS source files into dist/css/
  4. Generates dist/index.d.ts (single rolled-up declaration file)

Publishing to npm

# Bump version (patch | minor | major)
npm version patch

# Build
npm run build

# Publish
npm publish --access public

The "files": ["dist"] field in package.json ensures only the dist/ directory is included in the published package — no source files, test files, or config.

Versioning

| Change type | Bump | |---|---| | Bug fix, style tweak | patch | | New component, new theme, new prop | minor | | Breaking API change | major |


License

MIT