Oihana Next UI - 0.48.0

Application logo

Oihana Next UI

Oihana Next UI

A modular Next.js UI component library built with React 19, Tailwind CSS v4 and DaisyUI v5.

GitHub Demo

npm version npm downloads License

Features

  • ⚛️ React 19 + Next.js 16
  • 🎨 Tailwind CSS v4 + DaisyUI v5
  • 🌙 Dark mode support (flash-free)
  • 📱 PWA ready
  • 📴 Works offline — actions queued and sent when the network is back, records kept and searched
    on the device, a page served when a navigation fails (offline)
  • ♿ Accessible components
  • 🧩 Composable and themeable

Installation

bun add oihana-next-ui
# or
npm install oihana-next-ui

next, react and react-dom are peer dependencies : the library installs
none of them, your application provides them — in a single copy.

Requirements

Your application must already depend on :

{
  "react":     "^19.0.0",
  "react-dom": "^19.0.0",
  "next":      "^16.0.0"
}

Usage

import Picture   from 'oihana-next-ui/components/images/Picture'
import Button    from 'oihana-next-ui/components/Button'
import useThemes from 'oihana-next-ui/contexts/themes/useThemes'

Every component carries its own JSDoc — props, defaults and examples — and the live demos
sit under /components and /modules, with /scenarios bringing several of them together on
screens of use : a CRM, dashboards, a checkout, logistics. The wiki holds
the long-form guides, one per group : which component to reach for, and the recipes that span
several of them. Start with setting up an application,
then the groups :
components/barcodes,
components/charts,
components/maps,
components/documents,
components/media,
components/metrics,
components/scheduler,
contexts/feedback,
contexts/locale,
contexts/navigation and
offline.

Scripts

Five build-time scripts ship with the package, as commands run from the root of
the host application :

CommandWhat it writes
oihana-inject-versionsrc/version.js, public/version.json (version and the deployed commit) and public/sw.js, from version and the pwa block of your package.json (offline, cachePrefix, offlineShell)
oihana-cache-sizenothing — it reports the weight of .next and its Turbopack caches (--all for the full breakdown)
oihana-generate-splashthe iOS splash screens under public/assets/splash (--bg, --landscape, --force) ; requires sharp in the host
oihana-copy-maplibre-workerthe map engine's worker under public/maplibre/ — only needed with maplibre-gl 6, which loads its worker from a file ; the library stays on 5, which needs none — see the maps guide
oihana-copy-zxing-wasmthe barcode reader's WebAssembly under public/zxing/<version>/, so the camera scanner reads without a CDN and offline ; exits 0 when the optional zxing-wasm is absent — see the barcodes guide
oihana-copy-pdfjs-assetsthe PDF engine's worker, WebAssembly decoders and fonts under public/pdfjs/<version>/, so a document opens without a CDN and offline ; exits 0 when the optional react-pdf is absent
{
  "scripts": {
    "dev": "oihana-inject-version && next dev",
    "predev": "oihana-cache-size && oihana-copy-zxing-wasm && oihana-copy-pdfjs-assets",
    "prebuild": "oihana-copy-zxing-wasm && oihana-copy-pdfjs-assets",
    "generate-splash": "oihana-generate-splash --bg \"#ffffff\""
  },
  "pwa": { "offline": true, "cachePrefix": "my-app" }
}

The service-worker templates come from the package, so a host keeps no copy of
its own. With offline, the worker keeps each page visited, Next's scripts and styles, the
barcode reader and the PDF engine, and serves them without a network. Add "offlineShell" : "/offline" and it also keeps that page at install — a page of yours — and sends any navigation
that fails to it, the page asked for in ?from= : a page never visited shows your words, not the
browser's error screen.

Default labels

Components read their labels — button names, tooltips, aria-labels — from the
host's i18n dictionary, under components.<family> (components.modal,
components.pagination, components.buttons.theme…). The library ships a
French / English bundle for each family ; merge them into your own components
entry rather than copying them, so keys added upstream reach you on upgrade :

// @locale/components/index.js
import components from 'oihana-next-ui/locale/components/index' ;

import auth from './auth' ; // your own component bundles

export default { ...components , auth } ;

A family you redefine replaces the library's one as a whole. The
translations guide covers the rest : writing the bundles, the
server side, numbers and dates.

CSS

The base layer — the theme-dark variant, the .z-* / .rotate-* scales the
components use, text-glow, the heading scale, the fullscreen backdrop and the
scrollbar — ships as one file. No colour and no font live in it :

/* globals.css */
@import 'tailwindcss' ;
@import 'oihana-next-ui/themes/base.css' ;

Some components require their stylesheet to be imported explicitly :

/* Spinner styles */
@import 'oihana-next-ui/components/spinners/styles/battery.css';

Tailwind CSS v4 plugins

With Tailwind CSS v4, plugins are registered directly in your CSS file :

/* globals.css */
@import 'tailwindcss' ;

/* Oihana background patterns plugin */
@plugin 'oihana-next-ui/themes/plugins/background-patterns/index' ;

/* Oihana pattern craft plugin */
@plugin 'oihana-next-ui/themes/plugins/pattern-craft/index' ;

DaisyUI themes

Each palette is a file of its own, next to the others — take one, or write your
own beside your @plugin "daisyui" block, which stays yours :

/* globals.css */
@import 'oihana-next-ui/themes/daisyui/oihana.css' ;     /* oihana-ui-light / oihana-ui-dark */
@import 'oihana-next-ui/themes/daisyui/catppuccin.css' ; /* the four Catppuccin flavours */

Offline

A screen held in a hand on a dock, in a van or in a cellar loses the network, and must not lose
the work. Everything the library does for it, at a glance — the offline guide
says how the pieces fit :

NeedPieces
Writing without a networkthe outbox — OutboxProvider, useOutbox, OutboxBanner, OutboxList, on oihana-next-ui/offline/outbox/* : each action kept on the device (IndexedDB, files included), sent in order when the server answers, never twice (Idempotency-Key), with dependencies, replacement, retries and accounts ; your transport — an HTTP API or Server Actions
Reading without a networkthe reserve — OfflineStoreProvider, useOfflineRecord, useOfflineQuery, on oihana-next-ui/offline/store/* : the records seen or prepared for the day, aged and swept, and searched on the device (text without accents, filters, sort)
A page that does not exist offlinethe service worker's shell — pwa.offlineShell : kept at install, shown with the page asked for
Knowing the network is really thereuseOnline, ConnectionStatus : the server asked, not just the Wi-Fi
Scanning, reading a PDFoihana-copy-zxing-wasm, oihana-copy-pdfjs-assets : the engines served by your application, kept by the worker
A screen that stays onuseWakeLock, WakeLockToggle

The core of the outbox and the reserve (offline/*) imports nothing but idb : no React, so it
also runs outside a component.

Development

Clone the repo and start the demo app :

git clone https://github.com/BcommeBois/oihana-next-ui
cd oihana-next-ui
bun install
bun dev

Open http://localhost:3666 to browse the component demos.

Built with

  • Next.js — React framework for production
  • React — UI library
  • Tailwind CSS — Utility-first CSS framework
  • DaisyUI — Component library for Tailwind CSS
  • VEGAS Core JS — A set of libraries writing in Javascript and based on the ES6 standard.
  • React Icons — Popular icon packs as React components
  • Motion — Animation library
  • Maskito — Input masking library
  • Day.js — Lightweight date library
  • Chroma.js — Color manipulation library

Release

The package publishes the raw src/ tree (no build step) — see the files and exports fields in package.json.

Versioning

This project follows Semantic Versioning — MAJOR.MINOR.PATCH (e.g. 1.2.3).

TypeCommandExampleWhen to use
Patchbun run release:patch0.1.46 → 0.1.47Bug fix, small tweak
Minorbun run release:minor0.1.46 → 0.2.0New component or feature, backward compatible
Majorbun run release:major0.1.46 → 1.0.0Breaking change

Prerequisites

  • Logged in to npm — npm whoami should print your username (otherwise npm login).
  • A second factor the registry accepts — a publish is refused without one, whatever npm whoami says. See Publishing credentials.
  • A git remote named origin-ssh configured (the release script pushes there with --follow-tags).
  • A clean working tree, ideally — release:* will otherwise commit any pending change as chore: prepare release before bumping the version.

Patch release walkthrough — e.g. 0.1.46 → 0.1.47

  1. Update CHANGELOG.md — add a new section under [Unreleased] with the new version and date :

    ## [0.1.47] — 2026-04-27
    
    **Components**
    - `XYZ` — what changed and why.
  2. Run the release script :

    bun run release:patch

    What happens, in order — all of this is automatic :

    1. stage — commits any pending change as chore: prepare release (skipped if the working tree is clean).
    2. npm version patch — bumps 0.1.46 → 0.1.47 in package.json.
    3. version script (auto-run by npm version) :
      • inject-version writes the new version into src/version.js and public/sw.js,
      • generate-exports refreshes the exports field in package.json,
      • then stages src/version.js, public/sw.js and package.json for the version commit.
    4. npm version creates the release commit (0.1.47) and the matching git tag.
    5. postversion script (auto-run by npm version) → release :
      • npm publish --access public publishes to npm,
      • git push origin-ssh --follow-tags pushes the commit and the tag to GitHub.

Publishing credentials

npm profile get prints what the account requires. With two-factor auth: auth-and-writes, every
publish needs a second factor, and there are two ways to give one.

A one-time code, from the authenticator registered on the account :

npm publish --access public --otp=123456

The code lives about thirty seconds — read it right before running the command.

A granular access token that bypasses 2FA, which is what a publish run from a script needs. On
npmjs.com → Access Tokens → Generate New Token → Granular Access Token :

  • scope it to this package alone, permission Read and write — never « all packages » ;

  • enable the option letting the token bypass two-factor authentication, or the publish is refused
    just the same ;

  • set the longest expiry offered, and write the date down ;

  • copy the token — it is shown once — into your own ~/.npmrc, never the repository's :

    //registry.npmjs.org/:_authToken=npm_xxxxxxxx
    

🚨 npm login overwrites the token. Both write the SAME line of ~/.npmrc, so signing in —
in any form, web included — replaces a granular token with an ordinary session token, which does
not bypass two-factor authentication. A publish that worked a minute earlier then fails with the
403 below, and nothing on screen connects the two. Once the token is installed, do not run
npm login again
unless you mean to put the token back afterwards.

🚨 A token expires, and the day it does the publish stops with an error that says nothing about
expiry.
It is the likeliest reason a release that worked last month fails today. Check the token's
date before looking anywhere else.

⚠️ npm is restricting bypass-2FA tokens — account changes since August 2026, direct publishing from
January 2027. An authenticator or a passkey is the answer that will still be there afterwards.

When a publish fails

The version has already been bumped by then. npm version commits and tags before postversion
runs the publish, so a failed publish leaves package.json, src/version.js, the release commit and
the tag all correct and only the registry behind. Do not run release:* again — it would bump a
second time and leave an orphan tag. Fix the cause, then publish that same version on its own :

npm publish --access public
What npm printsWhat it meansWhat to do
E404 … Not Found - PUTNot authenticated. On a publish the registry answers 404 rather than 401, so it never reveals whether a package exists. The message is misleading on purpose.npm whoami — it fails too. Then npm login.
E403 … Two-factor authentication or granular access token with bypass 2fa enabled is requiredAuthenticated, but no second factor was given. Either the token expired, or an npm login overwrote it.See Publishing credentials — and check that ~/.npmrc still holds the granular token, not a session one.
EPRIVATE … This package has been marked as privateYou are in the wrong repository. npm checks that flag before uploading anything, so nothing left the machine.npm pkg get name before anything else. Leave the private flag where it is — it is a guard, and it just did its job.
E403 … you do not have permissionThe account does not own the package, or the token's scope leaves it out.npm owner ls oihana-next-ui, then check the token's package list.

What the registry actually holds — the answer, not the attempt :

npm view oihana-next-ui version --prefer-online

--prefer-online matters : the local cache will otherwise happily show yesterday's version.

Manual / pre-release version

Set a specific version manually — version + postversion still run as above :

npm version 1.0.0

Pre-release versions :

npm version prerelease --preid=alpha   # 0.1.46 → 0.1.47-alpha.0
npm version prerelease --preid=beta    # 0.1.46 → 0.1.47-beta.0
npm version prerelease --preid=rc      # 0.1.46 → 0.1.47-rc.0

License

Mozilla Public License 2.0 — © Marc Alcaraz