Frontend Implementation Guide
Status
- document type: current frontend rules plus implementation guidance
- source of truth for runtime stack: code in
package.json,vite.config.mts,tailwind.config.js, andapp/javascript - use this page when the task touches dashboard, widget, portal, survey, or SDK frontend work; component sourcing; state management; or frontend library choices
- use Frontend Agent Playbook for the execution workflow that decides reuse, extension, placement, and verification
- use Frontend Dependency Policy when deciding whether a new package should be adopted
- use Dashboard Feature Template for the default module structure of a new dashboard feature
- use Design Tokens And UI Conventions for practical styling decisions
- use Implementation Examples Map for concrete code references
Current Frontend Runtime
The current codebase frontend is:- a Vue 3 + Vite frontend attached to a Rails monolith rather than a standalone SPA platform
- split into dashboard, widget, portal, survey, and SDK entrypoints
- booted in the dashboard with both
VuexandPinia, because migration is incremental rather than complete - organized around several UI layers instead of one fully unified design system
- already using
VueUseas the main utility/composable layer for interactive behavior and browser APIs
Implemented Frontend Layers
Shared UI Layers
app/javascript/dashboard/components-next/is the preferred shared dashboard UI layer for new reusable componentsapp/javascript/dashboard/components/ui/contains older shared wrappers and utilities still in useapp/javascript/dashboard/components/is legacy/shared dashboard UI that still powers existing surfaces- route-local components under
routes/or feature folders are common for feature-specific composition
Styling
- Tailwind 3 is already installed and should be preferred for new or heavily reworked surfaces
- existing SCSS and scoped styles still exist, especially on older surfaces
- shared visual tokens already exist through the project theme and
n-*Tailwind classes - dark mode is already supported through existing token usage and should not be broken by new work
State And Data
- legacy dashboard surfaces still use
Vuex - newer work can use
Pinia app/javascript/dashboard/store/storeFactory.jsalready provides a bridge pattern so new CRUD stores can be created withPiniawithout rewriting unrelatedVuexmodules- dashboard API clients already live under
app/javascript/dashboard/api/
Forms And Validation
Vuelidateis the active default validation pattern across many dashboard surfacesFormKitexists in the repo and is useful for targeted schema-driven or integration-specific casesFormKitshould not be treated as the default architecture for all new dashboard forms
Visual Development And Tests
Histoireis already configured and should be used for reusable components and visually complex feature componentsVitestis already configured for frontend unit and component tests
Non-Goals
This guide does not recommend:- introducing
Nuxtas a second application runtime for dashboard features - replacing the whole dashboard UI layer with a third-party component kit
- spreading raw third-party primitives directly through feature code without local wrappers
- forcing a full
VuextoPiniarewrite before shipping new product work
Frontend Implementation Rules
App Shell And Build
- keep
Rails + Vue 3 + Viteas the frontend platform - keep frontend changes additive and aligned with existing account-scoped routing and boot payloads
- prefer shipping a coherent feature inside the current shell over building a parallel app runtime
Shared UI Layer Rules
Use this order when deciding where new UI code should live:- existing
components-nextprimitive or feature component - new shared reusable component in
components-next - route-local component for feature-specific composition
- legacy
components/only when extending an already-legacy surface
components-next rather than using a raw third-party component everywhere.
State And Data Rules
- default new feature state to
Pinia - use
storeFactorywhen the feature matches the existing CRUD store pattern - read existing
Vuexbootstrap or account state through a small composable or bridge when needed - do not migrate unrelated
Vuexmodules as part of a feature unless the feature is blocked without it - keep API clients in
app/javascript/dashboard/api/ - keep feature stores in
app/javascript/dashboard/stores/ - keep reusable logic in
app/javascript/dashboard/composables/or feature-local composables when that is clearer
Component Sourcing Order
When designing or implementing a new component, use sources in this order:- existing
components-nextcomponents and Histoire stories - existing route screens or feature modules that already solve a similar interaction
VueUsefor composables, directives, interaction helpers, and browser APIsreka-uifor accessible headless primitivesoriginui-vuefor copy-paste Vue component composition and polished Tailwind app patternsshadcn-vuefor recipes, APIs, and code patternsInspira UIfor visual inspiration and motion ideas- feature-local specialist libraries only after checking that the current stack and wrappers are insufficient
External Source Policy
VueUse
Use VueUse as the preferred external source for:
- composables and directives
- browser API wrappers
- outside-click behavior
- resize, scroll, and viewport helpers
- storage, debounce, throttle, and small UI state helpers
- route/query synchronization helpers when they fit the existing router flow
VueUse as a utility layer, not as a visual design system.
reka-ui
Use reka-ui as the preferred external source for accessible headless primitives such as:
- dialog
- popover
- dropdown menu
- tooltip
- tabs
- accordion
- select
- combobox
- checkbox and switch style primitives where headless behavior matters
reka-ui usage inside local components-next components so the external library does not become the public UI API of the app.
originui-vue
Use originui-vue as:
- a copy-paste Vue component source
- a visual and composition reference for polished Tailwind app UI
- a source of ideas for inputs, lists, settings surfaces, cards, checkboxes, radios, switches, and other app-shell patterns
originui-vue as a second runtime UI kit. Adapt its ideas to the current Tailwind 3 setup, local tokens, and local wrappers instead of importing its design system wholesale.
shadcn-vue
Use shadcn-vue as:
- a recipe source
- an API design reference
- a code pattern library for how a component can be composed
shadcn-vue as a second design system that should be imported wholesale into onelink.
Inspira UI
Use Inspira UI as:
- visual inspiration
- motion and presentation inspiration
- a source for specific aesthetic ideas when a surface needs to feel more polished or intentional
Inspira UI as the architectural base for dashboard/admin UI, and do not import a Nuxt-oriented app shell into the Rails dashboard.
Implemented Reference Module
The native scheduling module is now the strongest in-repo reference for a new account-scoped dashboard feature that combines:Piniastores- feature-local API clients
- reusable
components-nextfeature primitives - route-level pages
- backend-driven CRUD and finance flows
- routes:
app/javascript/dashboard/routes/dashboard/scheduling/ - feature components:
app/javascript/dashboard/components-next/Scheduling/ - stores:
app/javascript/dashboard/stores/scheduling/ - API clients:
app/javascript/dashboard/api/scheduling/
Specialist Libraries
Specialist libraries such as calendar, scheduler, kanban, or drag-and-drop packages may be used when the feature genuinely needs them, but they should be treated as feature-local dependencies unless they prove reusable across multiple product surfaces.Styling And Token Rules
- prefer Tailwind utilities for new work
- reuse existing project colors, spacing, radius, and token conventions before inventing a parallel token layer
- preserve existing dark/light behavior
- if a reusable component needs variant logic, keep the API local and readable first
- do not introduce a new class utility stack unless several new reusable components clearly justify it
Forms And Validation Rules
- default to
components-nextinputs, textareas, dialogs, selects, and validation withVuelidate - use
FormKitonly where schema-driven or integration-generated forms clearly benefit from it - keep validation logic close to the form or feature composable
Stories, Testing, And Verification
- add or update
Histoirestories for reusable primitives and visually complex feature components - use
Vitestfor API clients, composables, and non-trivial component behavior - for route-level features, run a manual smoke pass in the real dashboard in addition to stories
Shared Platform Versus Domain UI
- shared platform screens, primitives, filters, list patterns, and workflow shells belong in shared dashboard code
- domain-specific sections, vocabulary, validation rules, and composition should sit on top of shared screens instead of forking the whole module
- if a capability is needed by multiple domain profiles, move it into shared frontend code first
Recommended Structure For A New Frontend Module
- shared primitives in
components-next - feature-specific screens in
routes/dashboard/... - feature stores in
stores - API clients in
api - stories next to reusable components
Delivery Sequence For A New Frontend Feature
- confirm the backend contract and create the frontend API client
- identify reusable components that already exist in
components-next - add missing wrappers or primitives in
components-next, ideally with Histoire stories - build the
Piniastore or feature composables - assemble route-level pages, drawers, sidebars, or detail panels
- add domain-specific overlays only after the shared happy path works
- verify with Histoire, Vitest where useful, and a dashboard smoke check
Known Gaps
These are still weak spots in the repo and should be treated as follow-up work rather than assumed solved:- there is no dedicated design-token guide beyond
tailwind.config.jsand the current component code - there is no official code generator for new dashboard features yet