Noundry.ts Styling Guide

Choose Your Styling Approach: shadcn/ui vs. Noundry Default

Two Approaches, One Component Library

Noundry.ts UI components support two styling approaches:

  • shadcn/ui Style (BasecoatUI) - Component classes like .input, .btn, .card
  • Noundry Default Style - Standard Tailwind utility classes

Both use Tailwind CSS under the hood, so you can mix and match as needed!

Which Approach Should You Choose?

shadcn/ui Style

When to Use

  • ✅ You want a polished, professional look out-of-the-box
  • ✅ You prefer component classes over utility classes
  • ✅ You're building a SaaS product or dashboard
  • ✅ You like the shadcn/ui aesthetic
  • ✅ You want less markup in your code

Example:

class="btn btn-primary"
Noundry Default

When to Use

  • ✅ You need maximum design flexibility
  • ✅ Your team is familiar with Tailwind CSS
  • ✅ You want fine-grained control over every style
  • ✅ You're building custom-branded experiences
  • ✅ You prefer utility-first CSS

Example:

class="px-4 py-2 bg-blue-600 text-white rounded-lg"

Side-by-Side Component Examples

See how to use each styling approach with Noundry.ts TypeScript components

📝 Input Component

shadcn/ui Style
TypeScript
import { Input } from './ui-components/src';

const emailInput = Input.create({
  name: 'email',
  type: 'email',
  placeholder: 'name@example.com',
  inputClass: 'input',  // BasecoatUI class
  icon: 'envelope',
  iconPosition: 'left'
});

document.getElementById('form')
  .appendChild(emailInput);
Noundry Default
TypeScript
import { Input } from './ui-components/src';

const emailInput = Input.create({
  name: 'email',
  type: 'email',
  placeholder: 'name@example.com',
  // No inputClass - uses Tailwind defaults
  icon: 'envelope',
  iconPosition: 'left'
});

document.getElementById('form')
  .appendChild(emailInput);

Key Difference:

With shadcn/ui, add inputClass: 'input'. Without it, Noundry uses beautiful Tailwind defaults automatically.

📊 DataTable Component

shadcn/ui Style
TypeScript
import { DataTable } from './ui-components/src';

const table = DataTable.create({
  title: 'Users',
  columns: [
    { key: 'id', label: 'ID' },
    { key: 'name', label: 'Name' }
  ],
  class: 'table-container',
  tableClass: 'table',
  // Uses BasecoatUI table styling
  apiEndpoint: '/api/users'
});

document.getElementById('app')
  .appendChild(table);
Noundry Default
TypeScript
import { DataTable } from './ui-components/src';

const table = DataTable.create({
  title: 'Users',
  columns: [
    { key: 'id', label: 'ID' },
    { key: 'name', label: 'Name' }
  ],
  // No class overrides - uses Tailwind defaults
  apiEndpoint: '/api/users',
  pagination: true,
  searchable: true
});

document.getElementById('app')
  .appendChild(table);

Available Class Overrides:

  • tableClass - Override the table element
  • headerClass - Override header cells
  • cellClass - Override body cells
  • paginationClass - Override pagination container

🎯 MultiSelect Component

shadcn/ui Style
TypeScript
import { MultiSelect } from './ui-components/src';

const userSelect = MultiSelect.create({
  name: 'users[]',
  placeholder: 'Select users...',
  buttonClass: 'select-trigger',
  // BasecoatUI select styling
  apiEndpoint: '/api/users',
  onChange: (selected) => {
    console.log('Selected:', selected);
  }
});

document.getElementById('form')
  .appendChild(userSelect);
Noundry Default
TypeScript
import { MultiSelect } from './ui-components/src';

const userSelect = MultiSelect.create({
  name: 'users[]',
  placeholder: 'Select users...',
  // Uses Tailwind defaults automatically
  apiEndpoint: '/api/users',
  onChange: (selected) => {
    console.log('Selected:', selected);
  }
});

document.getElementById('form')
  .appendChild(userSelect);

Available Class Overrides:

  • buttonClass - Override the trigger button
  • dropdownClass - Override the dropdown container
  • optionClass - Override individual options

📅 DatePicker Component

shadcn/ui Style
TypeScript
import { DatePicker } from './ui-components/src';

const datePicker = DatePicker.create({
  name: 'birthdate',
  placeholder: 'Select date',
  format: 'YYYY-MM-DD',
  inputClass: 'input',
  // BasecoatUI input styling
  onChange: (value) => {
    console.log('Selected:', value);
  }
});

document.getElementById('form')
  .appendChild(datePicker);
Noundry Default
TypeScript
import { DatePicker } from './ui-components/src';

const datePicker = DatePicker.create({
  name: 'birthdate',
  placeholder: 'Select date',
  format: 'YYYY-MM-DD',
  // Uses Tailwind defaults automatically
  onChange: (value) => {
    console.log('Selected:', value);
  }
});

document.getElementById('form')
  .appendChild(datePicker);

Available Class Overrides:

  • inputClass - Override the input field
  • calendarClass - Override the calendar dropdown

🪟 Modal Component

shadcn/ui Style
TypeScript
import { FullScreenModal } from './ui-components/src';

const modal = FullScreenModal.create({
  title: 'Preview',
  overlayClass: 'dialog-overlay',
  contentClass: 'dialog-content',
  // BasecoatUI dialog styling
  onClose: () => {
    console.log('Modal closed');
  }
});

modal.setContent('<p>Content here</p>');
modal.open();
Noundry Default
TypeScript
import { FullScreenModal } from './ui-components/src';

const modal = FullScreenModal.create({
  title: 'Preview',
  // Uses Tailwind defaults automatically
  onClose: () => {
    console.log('Modal closed');
  }
});

modal.setContent('<p>Content here</p>');
modal.open();

Available Class Overrides:

  • overlayClass - Override the backdrop/overlay
  • contentClass - Override the modal content wrapper
  • headerClass - Override the header section

Complete Component Reference

All available class override props for each component

Component Class Override Props BasecoatUI Equivalent
Input inputClass iconClass input
DataTable tableClass headerClass cellClass paginationClass table table-container
MultiSelect buttonClass dropdownClass optionClass select-trigger
SearchSelect buttonClass dropdownClass optionClass select-trigger
FullScreenModal overlayClass contentClass headerClass dialog-overlay dialog-content
DatePicker inputClass calendarClass input
DateRangePicker inputClass calendarClass input
SlideOver overlayClass panelClass Custom
NavigationMenu menuClass itemClass Custom
Pacer barClass Custom

Can You Mix Both Approaches?

Yes! Mix and match freely.

Both approaches use Tailwind CSS, so you can use BasecoatUI classes for some components and Noundry defaults for others in the same project.

TypeScript - Mixing Both Approaches
// Mix BasecoatUI and Noundry styling in the same form

const form = document.getElementById('myForm');

// Use BasecoatUI for this input
const emailInput = Input.create({
  name: 'email',
  inputClass: 'input'  // BasecoatUI
});

// Use Noundry default for this input
const passwordInput = Input.create({
  name: 'password'
  // No inputClass - Noundry defaults
});

// Use BasecoatUI for the table
const table = DataTable.create({
  columns: [...],
  class: 'table-container',
  tableClass: 'table'  // BasecoatUI
});

form.appendChild(emailInput);
form.appendChild(passwordInput);
form.appendChild(table);

Quick Start Guide

🎨 shadcn/ui Style Setup

  1. 1. Include BasecoatUI CSS in your HTML:
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/basecoat-ui@1.7.0/dist/basecoat.css">
  2. 2. Add class overrides to components:
    inputClass: 'input'
  3. 3. Use BasecoatUI classes throughout your components

⚡ Noundry Default Setup

  1. 1. Include Tailwind CSS in your HTML:
    <script src="https://cdn.tailwindcss.com"></script>
  2. 2. Use components without class overrides:
    Input.create({ name: 'email' })
  3. 3. Beautiful Tailwind defaults applied automatically

See Both Styles in Action

Check out the live demo pages to see each styling approach:

Summary

shadcn/ui Style: Use component classes (inputClass: 'input') for a polished, professional look

Noundry Default: Omit class overrides to use beautiful Tailwind defaults automatically

🎨 Mix Both: Use different approaches for different components in the same project

🚀 No Breaking Changes: All class overrides are optional - existing code works unchanged