Fondasi Design System

Fondasi adalah design system berbasis JavaScript dan CSS untuk membangun antarmuka pengguna yang modern dan konsisten dengan nuansa Indonesia.

Fitur Utama

  • 12+ komponen UI yang siap pakai
  • Didukung JavaScript (UMD) dan CSS
  • Responsif dan aksesibel
  • Customizable melalui variabel CSS
  • Mendukung light mode dan dark mode

Struktur File

  • dist/fondasi.css - Full CSS
  • dist/fondasi.min.css - Minified CSS
  • dist/fondasi.js - Full JavaScript
  • dist/fondasi.min.js - Minified JavaScript

Quick Start

<link rel="stylesheet" href="fondasi.css">
<script src="fondasi.js"></script>

Installation

Ada beberapa cara untuk menginstall Fondasi ke dalam project Anda.

Cara 1: CDN

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/bideyesa/fondasi@v1.0.0/dist/fondasi.min.css">
<script src="https://cdn.jsdelivr.net/gh/bideyesa/fondasi@v1.0.0/dist/fondasi.min.js"></script>

Cara 2: NPM

npm install fondasi
import 'fondasi/dist/fondasi.css';
import { initComponents } from 'fondasi';

initComponents();

Cara 3: Download Langsung

  • dist/fondasi.css
  • dist/fondasi.min.css
  • dist/fondasi.js
  • dist/fondasi.min.js

Colors

Palette warna profesional yang menggunakan CSS custom properties. Klik untuk copy.

Brand Palette

brand-darker #0d1c2e
brand-dark #152a45
brand-default #1e3a5f
brand-mid #94a8c2
brand-light #e8eef4
accent #2563eb

Semantic Colors

Success --color-success
#2d8a4e Bg: #edf7f2
Warning --color-warning
#c7810e Bg: #fef5e6
Danger --color-danger
#c42b2b Bg: #fdf2f2
Info --color-info
#2563a8 Bg: #eef4fb

Surface & Background

surface #ffffff
surface-2 #f6f5f4
surface-3 #efeeed
surface-4 #e5e4e2
bg #fafaf9

Text Colors

Text Default - The quick brown fox
Text 2 - The quick brown fox
Text 3 - The quick brown fox
Text 4 - The quick brown fox
Token Value
--color-text-default#1a1816
--color-text-2#44403c
--color-text-3#78716c
--color-text-4#a8a29e
--color-text-inv#ffffff

Border Colors

default #e0ddd9
strong #c9c5c1
subtle #ebe9e7

Typography

Sistem tipografi yang menggunakan IBM Plex Sans sebagai font default dengan kontras yang jelas.

Font Families

ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789
IBM Plex Sans --font-sans
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789
Libre Baskerville --font-serif
function hello() {'{'}
  return "world";
{'}'}
IBM Plex Mono --font-mono

Font Sizes - Visual Scale

--text-4xl Heading 1 36px
--text-3xl Heading 2 30px
--text-2xl Heading 3 24px
--text-xl Heading 4 20px
--text-lg Large Text 18px
--text-md Medium Text 16px
--text-base Base Text 14px
--text-sm Small Text 12px
--text-xs Extra Small 11px

Font Weights

Light - The quick brown fox 300
Normal - The quick brown fox 400
Medium - The quick brown fox 500
Semi Bold - The quick brown fox 600
Bold - The quick brown fox 700

Line Heights

Tight (1.25)

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt.

Normal (1.5)

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt.

Loose (1.75)

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt.

Spacing

Sistem spacing yang konsisten untuk menjaga alignment dan proximity yang tepat.

Spacing Scale - Visual

space-1
4px
space-2
8px
space-3
12px
space-4
16px
space-5
20px
space-6
24px
space-8
32px
space-10
40px
space-12
48px

Padding Examples

p-1 (4px)
p-2 (8px)
p-3 (12px)
p-4 (16px)
p-5 (20px)
p-6 (24px)

Margin Examples

No margin
m-2 (8px)
m-4 (16px)
mx-auto

Gap Examples

gap-2
gap-4
gap-6

Buttons

Komponen button dengan berbagai variant dan ukuran.

Class Reference

Class Description
.btnDefault button
.btn-primaryPrimary brand color
.btn-secondarySecondary variant
.btn-ghostGhost variant
.btn-dangerDanger/destructive
.btn-smSmall button
.btn-mdMedium (default)
.btn-lgLarge button

Variant: Default

<button class="btn" data-nusa-button>Default Button</button>

Variant: Primary

<button class="btn btn-primary" data-nusa-button>Primary Button</button>

Variant: Secondary

<button class="btn btn-secondary" data-nusa-button>Secondary Button</button>

Variant: Ghost

<button class="btn btn-ghost" data-nusa-button>Ghost Button</button>

Variant: Danger

<button class="btn btn-danger" data-nusa-button>Danger Button</button>

Size: Small

<button class="btn btn-sm" data-nusa-button>Small Button</button>

Size: Medium (Default)

<button class="btn btn-md" data-nusa-button>Medium Button</button>

Size: Large

<button class="btn btn-lg" data-nusa-button>Large Button</button>

Alert

Komponen alert untuk menampilkan pesan informasi, peringatan, kesalahan, atau konfirmasi kepada pengguna.

Class Reference

ClassKeterangan
.alertBase alert — wajib digunakan
.alert-infoVariant informasi (biru)
.alert-successVariant sukses (hijau)
.alert-warningVariant peringatan (kuning)
.alert-dangerVariant bahaya / error (merah)
.alert-brandVariant brand navy
.alert-iconWrapper ikon di dalam alert
.alert-contentWrapper teks (title + body)
.alert-titleJudul alert (bold)
.alert-dismissTombol tutup alert
.alert-smUkuran kecil
.alert-lgUkuran besar

Variants

Ini adalah pesan informasi.
Data berhasil disimpan.
Periksa kembali data sebelum melanjutkan.
Terjadi kesalahan. Silakan coba lagi.
Fitur baru telah tersedia di portal.
<div class="alert alert-info">Ini adalah pesan informasi.</div>
<div class="alert alert-success">Data berhasil disimpan.</div>
<div class="alert alert-warning">Periksa kembali data sebelum melanjutkan.</div>
<div class="alert alert-danger">Terjadi kesalahan. Silakan coba lagi.</div>
<div class="alert alert-brand">Fitur baru telah tersedia di portal.</div>

Dengan Ikon

Sesi Anda akan berakhir dalam 5 menit.
Pendaftaran berhasil. Silakan cek email Anda.
Email atau password salah.
<div class="alert alert-info">
    <span class="alert-icon">ℹ</span>
    <span>Sesi Anda akan berakhir dalam 5 menit.</span>
</div>

Dengan Title

Perhatian
Data yang dihapus tidak dapat dikembalikan.
Berhasil
Laporan telah dikirim ke pimpinan untuk ditinjau.
<div class="alert alert-warning">
    <div class="alert-content">
        <div class="alert-title">Perhatian</div>
        Data yang dihapus tidak dapat dikembalikan.
    </div>
</div>

Dengan Tombol Tutup

Pembaruan sistem dijadwalkan pada 20 Mei 2026.
<div class="alert alert-info">
    <div class="alert-content">
        Pembaruan sistem dijadwalkan pada 20 Mei 2026.
    </div>
    <button class="alert-dismiss" onclick="this.closest('.alert').remove()">✕</button>
</div>

Ukuran

Alert ukuran kecil.
Alert ukuran default.
Alert ukuran besar.
<div class="alert alert-info alert-sm">Alert ukuran kecil.</div>
<div class="alert alert-info">Alert ukuran default.</div>
<div class="alert alert-info alert-lg">Alert ukuran besar.</div>

Form Elements

Komponen form yang profesional, formal, dan minimalist dengan berbagai state dan variant.

Class Reference

Class Description
.form-inputText input
.form-input-smSmall input (36px)
.form-input-lgLarge input (52px)
.form-selectSelect dropdown
.form-select-smSmall select
.form-select-lgLarge select
.form-textareaTextarea
.form-textarea-smSmall textarea
.form-textarea-lgLarge textarea
.form-checkboxCheckbox label wrapper
.form-checkbox-inputCheckbox input
.form-radioRadio label wrapper
.form-radio-inputRadio input
.form-switchSwitch label wrapper
.form-switch-inputSwitch input
.form-switch-smSmall switch
.form-switch-lgLarge switch
.form-groupForm group wrapper
.form-group-errorError state group
.form-group-successSuccess state group
.form-labelForm label
.form-label-requiredRequired label marker
.form-hintHint text
.form-errorError message
.input-wrapInput with icon wrapper
.input-iconInput icon
.input-groupInput with prefix/suffix
.input-group-textInput group text

Text Input

<div class="form-group">
    <label class="form-label">Full Name</label>
    <input type="text" class="form-input" placeholder="Enter your full name">
    </div>

Input with Label Required

<label class="form-label form-label-required">Email Address</label>

Input Sizes

<input class="form-input form-input-sm">
<input class="form-input">
<input class="form-input form-input-lg">

Input with Hint

Must be at least 8 characters
<div class="form-group">
    <label class="form-label">Password</label>
    <input type="password" class="form-input" placeholder="Enter password">
    <span class="form-hint">Must be at least 8 characters</span>
    </div>

Input with Error

Please enter a valid email address
<div class="form-group form-group-error">
    <label class="form-label">Email</label>
    <input type="email" class="form-input form-input-error" value="invalid-email">
    <span class="form-error">Please enter a valid email address</span>
    </div>

Input with Success

Username is available
<div class="form-group form-group-success">
    <label class="form-label">Username</label>
    <input type="text" class="form-input" value="johndoe">
    <span class="form-hint" style="color: var(--color-success);">Username is available</span>
</div>

Disabled Input

<input type="text" class="form-input" disabled>
<select class="form-select" disabled>...</select>
<textarea class="form-textarea" disabled>...</textarea>

Input with Icon

<div class="input-wrap">
    <input type="text" class="form-input" placeholder="Search...">
    <span class="input-icon">
        <svg>...</svg>
    </span>
</div>

    <div class="input-wrap">
        <input type="password" class="form-input">
        <button class="input-action">...</button>
    </div>

Input Group (Prefix/Suffix)

https://
USD
<div class="input-group">
    <span class="input-group-text">https://</span>
    <input type="text" class="form-input">
</div>

    <div class="input-group">
        <input type="text" class="form-input">
        <span class="input-group-text">USD</span>
    </div>

Select

<div class="form-group">
    <label class="form-label">Country</label>
    <select class="form-select">
        <option value="">Select a country</option>
        <option value="id">Indonesia</option>
    </select>
</div>

Textarea

<textarea class="form-textarea" rows="4" placeholder="Enter your message..."></textarea>

Checkbox

<label class="form-checkbox">
    <input type="checkbox" class="form-checkbox-input">
    <span>I agree to the terms and conditions</span>
</label>

Checkbox Group

<div class="form-check-group">
    <label class="form-checkbox">
        <input type="checkbox" class="form-checkbox-input">
        <span>Technology</span>
    </label>
    ...
</div>

Radio

<div class="form-check-group">
    <label class="form-radio">
        <input type="radio" class="form-radio-input" name="payment">
        <span>Credit Card</span>
    </label>
    ...
</div>

Switch

<label class="form-switch">
    <input type="checkbox" class="form-switch-input">
    <span>Enable notifications</span>
</label>

<label class="form-switch form-switch-sm">...</label>
<label class="form-switch form-switch-lg">...</label>

Form Row (2 Columns)

<div class="form-row">
    <div class="form-group">
        <label class="form-label">First Name</label>
        <input type="text" class="form-input">
    </div>
    <div class="form-group">
        <label class="form-label">Last Name</label>
        <input type="text" class="form-input">
    </div>
</div>

Complete Form Example

JavaScript Components

import { CharCounter, PasswordToggle, SearchSelect, validateForm, setFieldError } from 'fondasi';

// Character counter for textarea
const counter = new CharCounter('#bio', { max: 200 });

// Password visibility toggle
const password = new PasswordToggle('#password-input');

// Searchable select (combobox)
const select = new SearchSelect('#country-select', {
    options: [
        { value: 'id', label: 'Indonesia' },
        { value: 'sg', label: 'Singapore' }
    ]
});

// Form validation
const form = document.getElementById('my-form');
form.addEventListener('submit', (e) => {
    if (!validateForm(form)) {
        e.preventDefault();
    }
});

// Set field error programmatically
setFieldError('#email-input', 'Invalid email format');

Search Select

Komponen input dengan fitur pencarian — tersedia dalam varian single-select dan multiple-select.

Class Reference

Class / AtributDescription
.search-selectContainer single search select
.search-inputInput teks pencarian
.search-select-dropdownPanel dropdown
.search-select-listDaftar pilihan
.search-select-itemItem pilihan (data-value wajib)
.search-select-emptyPesan empty state
.search-select-multiContainer multi search select
.search-select-tagsArea chips + input (multi)
.search-select-tagChip item terpilih (multi)
.search-select-tag-removeTombol hapus chip
data-nameNama field untuk hidden input
data-placeholderPlaceholder teks input
data-no-data-textPesan ketika list kosong
data-empty-textPesan ketika hasil pencarian kosong
data-value (multi)Nilai awal dipisah koma: "id,sg"

Single Search Select

Pilih satu item dari daftar dengan fitur pencarian.

<div class="search-select">
    <input type="hidden" name="country" value="">
    <input type="text" class="search-input" placeholder="Search...">
    <div class="search-select-dropdown" hidden>
        <div class="search-select-list">
            <div class="search-select-item" data-value="id">Indonesia</div>
            <div class="search-select-item" data-value="sg">Singapore</div>
        </div>
    </div>
</div>

Empty State (No Data)

Ketika list kosong, dropdown tetap tampil dengan pesan yang dapat dikustomisasi.

<div class="search-select"
     data-no-data-text="Tidak ada data tersedia."
     data-empty-text="Tidak ada hasil yang cocok.">
    <input type="hidden" name="field" value="">
    <input type="text" class="search-input" placeholder="Search...">
    <div class="search-select-dropdown" hidden>
        <div class="search-select-list"></div>
    </div>
</div>

Multiple Search Select

Pilih beberapa item sekaligus — setiap pilihan tampil sebagai chip yang bisa dihapus. Tekan Backspace untuk menghapus chip terakhir.

JavaScript
TypeScript
Python
Go
Rust
Java
Kotlin
Swift
<div class="search-select-multi" data-name="languages">
    <div class="search-select-item" data-value="js">JavaScript</div>
    <div class="search-select-item" data-value="ts">TypeScript</div>
    <div class="search-select-item" data-value="py">Python</div>
    <div class="search-select-item" data-value="go">Go</div>
</div>

Multiple Select — Initial Values

Set nilai awal dengan data-value dipisah koma.

Admin
Editor
Viewer
Moderator
Analyst
<div class="search-select-multi" data-name="roles" data-value="admin,editor">
    <div class="search-select-item" data-value="admin">Admin</div>
    <div class="search-select-item" data-value="editor">Editor</div>
    <div class="search-select-item" data-value="viewer">Viewer</div>
</div>

JavaScript API

import { getSearchSelect, getMultiSearchSelect } from 'fondasi';

// Single select
const ss = getSearchSelect('#my-select', {
    onSelect: ({ item }) => console.log('Selected:', item),
    onClear:  () => console.log('Cleared'),
});

ss.select('id');   // pilih by value
ss.clear();        // clear selection
ss.setItems([      // replace items
    { value: 'id', label: 'Indonesia' }
]);

// Multi select
const ms = getMultiSearchSelect('#my-multi', {
    onSelect: ({ item }) => console.log('Added:', item),
    onRemove: ({ item }) => console.log('Removed:', item),
});

ms.select('js');   // tambah by value
ms.deselect('js'); // hapus by value
ms.clear();        // hapus semua
console.log(ms.values); // ['ts', 'py']

Events

// Single select events
el.addEventListener('searchselect:select', e => console.log(e.detail.item));
el.addEventListener('searchselect:clear',  () => {});

// Multi select events
el.addEventListener('multisearchselect:select', e => console.log(e.detail.item));
el.addEventListener('multisearchselect:remove', e => console.log(e.detail.item));
el.addEventListener('multisearchselect:clear',  () => {});

Card

Komponen card untuk menampilkan konten dalam container.

Class Reference

Class Description
.cardCard container
.card-headerCard header
.card-bodyCard body
.card-footerCard footer

Default Card

Card Title

This is the card body content. You can put any content here.

<div class="card">
    <div class="card-header">
        <h3>Card Title</h3>
    </div>
    <div class="card-body">
        <p>This is the card body content.</p>
    </div>
    <div class="card-footer">
        <button class="btn btn-primary">Action</button>
    </div>
</div>

Card with Image

Card with Image

This card has an image header area that can contain a cover image.

<div class="card">
    <div class="card-image">
        <img src="..." alt="...">
    </div>
    <div class="card-body">...</div>
</div>

Badge

Komponen badge untuk menampilkan label atau status.

Class Reference

Class Description
.badgeDefault badge
.badge-successSuccess variant
.badge-warningWarning variant
.badge-dangerDanger variant
.badge-infoInfo variant

Variant: Default

Default
<span class="badge">Default</span>

Variant: Success

Success
<span class="badge badge-success">Success</span>

Variant: Warning

Warning
<span class="badge badge-warning">Warning</span>

Variant: Danger

Danger
<span class="badge badge-danger">Danger</span>

Variant: Info

Info
<span class="badge badge-info">Info</span>

Badge in Table

Status Description
Active Account is active
Pending Awaiting approval
Inactive Account disabled

Table

Komponen table dua tingkat: CSS-only untuk tampilan statis, dan DataTable (JavaScript) untuk fitur interaktif — live search, sorting, seleksi baris, bulk actions, row expand, paginas bernomor, export CSV, dan skeleton loading.

CSS Class Reference

ClassKeterangan
.tableTabel dasar — full-width, border-collapse
.table-smPadding lebih kecil (compact)
.table-lgPadding lebih besar
.table-stripedBaris genap diberi background alternatif
.table-borderedBorder di semua sisi setiap sel
.table-borderlessHapus semua border bawah baris
.table-hoverKursor pointer di setiap baris
.table-responsiveStacked card layout di mobile (<768px)
.table-containerWrapper overflow-x:auto untuk scroll horizontal
.table-wrapWrapper JS DataTable (border + rounded)
.table-toolbarBar di atas table (search, filter, actions)
.table-toolbar-left / -rightSection kiri / kanan dalam toolbar
.table-bulk-barBar bulk actions — muncul saat ada baris dipilih
.table-bulk-countTeks jumlah baris terpilih
.table-bulk-actionsKumpulan tombol bulk action
.table-bulk-dismissTombol batal pilih (X) di kanan bulk bar
.th-sortableTH yang dapat diklik untuk sort (ditambah JS)
.th-sort-iconIkon ▲▼ di dalam TH yang sortable
.table-selectableDitambahkan ke <table> saat seleksi aktif
tr.selectedBaris yang sedang dipilih
.tr-expandedBaris yang expandnya sedang terbuka
.tr-expand-rowBaris konten expand (disisipkan setelah baris utama)
.tr-expand-cellTD di dalam baris expand
.tr-skeletonBaris placeholder saat loading (shimmer animation)
.tr-emptyBaris empty state saat tidak ada data
.empty-iconIkon besar di empty state
.empty-titleTeks utama empty state
.empty-descTeks sekunder empty state (mis. hasil search)
.table-loadingModifier pada table-wrap saat loading aktif
.table-footerBar bawah table (info + pagination)
.table-footer-infoTeks "Menampilkan X–Y dari Z"
.table-filter-badgeBadge "terfilter" muncul saat search aktif
.table-footer-pagesKumpulan tombol nomor halaman
.table-page-ellipsisTanda … antar nomor halaman
.table-matchHighlight teks yang cocok dengan query search
.table-actionsFlex container untuk tombol aksi di setiap baris

Data Attribute Reference

AtributPada ElemenKeterangan
data-tablewrapperMenandai container sebagai DataTable (deklaratif)
data-sortable="false"wrapperNonaktifkan sorting (default: true)
data-selectable="true"wrapperAktifkan seleksi baris
data-paginate="true"wrapperAktifkan pagination client-side
data-page-size="10"wrapperJumlah baris per halaman
data-search-input="id"wrapperID dari <input> untuk live search
data-page-size-select-id="id"wrapperID dari <select> untuk pilih jumlah baris
data-expandable="true"wrapperAktifkan row expand
data-sort-col="nama"wrapperKolom sort awal
data-sort-dir="asc|desc"wrapperArah sort awal
data-empty-message="..."wrapperTeks empty state
data-col="key"<th>Kunci kolom untuk sorting
data-type="string|number|date"<th>Tipe data untuk algoritma sort
data-select-all<input type="checkbox"> di theadCheckbox select-all / deselect-all
data-row-check<input type="checkbox"> di tbodyCheckbox per baris
data-value="..."<td>Nilai override untuk sort/export (jika isi TD berbeda)
data-search-text="..."<td>Teks override untuk filter search
data-expand-btn<button> di barisTombol toggle expand baris
data-expand-content="html"<tr>Konten HTML yang ditampilkan saat expand
data-bulk-action="key"<button> di bulk barNama aksi yang dikirim ke callback onBulk
data-bulk-dismiss<button> di bulk barTombol untuk deselect semua baris
data-no-selectelemen dalam <tr>Klik elemen ini tidak memicu seleksi baris

Basic Table

Nama Email Peran Status
Andi Nugroho andi@email.com Admin Aktif
Siti Rahayu siti@email.com Editor Pending
Budi Prakoso budi@email.com Viewer Nonaktif
<table class="table">
    <thead>
        <tr>
            <th>Nama</th>
            <th>Status</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Andi Nugroho</td>
            <td><span class="badge badge-success">Aktif</span></td>
        </tr>
    </tbody>
</table>

Varian CSS

Striped

IDProdukHarga
001Produk ARp 100.000
002Produk BRp 200.000
003Produk CRp 150.000

Bordered

IDProdukHarga
001Produk ARp 100.000
002Produk BRp 200.000

Compact (table-sm)

IDProdukHarga
001Produk ARp 100.000
002Produk BRp 200.000
003Produk CRp 150.000
<table class="table table-striped">...</table>
<table class="table table-bordered">...</table>
<table class="table table-sm">...</table>
<table class="table table-lg">...</table>
<table class="table table-borderless">...</table>

DataTable — Demo Interaktif

Coba ketik di kolom pencarian, klik header untuk sort, pilih baris, atau klik ▶ untuk expand detail baris.

Nama Email Peran Bergabung Status Detail
Andi Nugroho andi@email.com Admin 12 Jan 2024 Aktif
Siti Rahayu siti@email.com Editor 8 Mar 2024 Pending
Budi Prakoso budi@email.com Viewer 24 Apr 2024 Nonaktif
Dewi Lestari dewi@email.com Manager 3 Jun 2024 Aktif
Reza Firmansyah reza@email.com Editor 17 Jul 2024 Pending
Fitri Handayani fitri@email.com Viewer 29 Agt 2024 Aktif
Hendra Kurniawan hendra@email.com Admin 5 Sep 2024 Nonaktif
Maya Sari maya@email.com Manager 11 Okt 2024 Aktif

Struktur HTML DataTable

<!-- 1. Search input (di luar wrapper, dihubungkan via ID) -->
<input type="text" id="mySearch" class="form-input form-input-sm" placeholder="Cari..." />

<!-- 2. Wrapper utama -->
<div id="myWrap" class="table-wrap"
     data-table
     data-selectable="true"
     data-paginate="true"
     data-page-size="10"
     data-search-input="mySearch"
     data-expandable="true">

    <!-- 3. Bulk action bar (opsional) -->
    <div class="table-bulk-bar" hidden>
        <span class="table-bulk-count"></span>
        <div class="table-bulk-actions">
            <button data-bulk-action="export">Ekspor</button>
            <button data-bulk-action="delete">Hapus</button>
        </div>
        <button data-bulk-dismiss>✕</button>
    </div>

    <div style="overflow-x:auto">
        <table class="table">
            <thead>
                <tr>
                    <!-- 4. Checkbox select-all -->
                    <th><input type="checkbox" data-select-all /></th>
                    <!-- 5. TH sortable: data-col + data-type -->
                    <th data-col="nama">Nama</th>
                    <th data-col="tanggal" data-type="date">Tanggal</th>
                </tr>
            </thead>
            <tbody>
                <!-- 6. Baris dengan expand content -->
                <tr data-expand-content="<p>Detail baris</p>">
                    <!-- 7. Checkbox per baris -->
                    <td><input type="checkbox" data-row-check /></td>
                    <td>Andi Nugroho</td>
                    <!-- 8. data-value untuk sort/export berbeda dari tampilan -->
                    <td data-value="2025-01-12">12 Jan 2025</td>
                    <td>
                        <!-- 9. Tombol expand -->
                        <button data-expand-btn aria-expanded="false">▶</button>
                        <!-- 10. Tombol lain: data-no-select agar tidak memicu seleksi -->
                        <button data-no-select>Hapus</button>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
    <!-- .table-footer dirender otomatis oleh JS -->
</div>

JavaScript — Opsi Constructor

OpsiTipeDefaultKeterangan
sortablebooleantrueAktifkan sort kolom (th[data-col])
selectablebooleanfalseAktifkan seleksi baris
multiSelectbooleantrueIzinkan memilih lebih dari satu baris
paginatebooleanfalseAktifkan pagination client-side
pageSizenumber10Jumlah baris per halaman
expandablebooleanfalseAktifkan row expand via data-expand-btn
searchInputElement | stringElemen atau ID <input> untuk live search
pageSizeSelectElement | stringElemen atau ID <select> pilih jumlah baris
sortColstringnullKolom sort awal (nilai data-col)
sortDir'asc' | 'desc''asc'Arah sort awal
loadingbooleanfalseMulai dalam state loading
emptyMessagestring'Tidak ada data.'Teks saat tidak ada baris
emptyIconstring'📭'Ikon/emoji saat tidak ada baris
onSortFunctionnullCallback ({col, dir, table})
onSelectFunctionnullCallback ({selected, row, table})
onPageFunctionnullCallback ({page, pageSize, table})
onSearchFunctionnullCallback ({query, table})
onBulkFunctionnullCallback ({action, selected, table})

JavaScript — Methods

MethodParameterKeterangan
search(query)stringFilter baris secara programatik
sort(col, dir?)string, 'asc'|'desc'Sort berdasarkan kolom
selectRow(target)Element | numberPilih satu baris
deselectRow(target)Element | numberBatalkan pilihan satu baris
selectAll()Pilih semua baris yang tampil
deselectAll()Batalkan semua pilihan
goTo(page)numberNavigasi ke halaman tertentu (1-based)
next()Halaman berikutnya
prev()Halaman sebelumnya
exportCSV(filename?)stringDownload baris yang tampil sebagai CSV
setLoading(state, n?)boolean, numberTampilkan/sembunyikan skeleton (n = jumlah baris skeleton)
setRows(rows)Element[] | stringGanti semua data baris secara programatik

JavaScript — Getters

GetterTipeKeterangan
.selectedRowsElement[]Array baris yang sedang dipilih
.selectedCountnumberJumlah baris yang dipilih
.currentPagenumberNomor halaman aktif (1-based)
.totalPagesnumberTotal halaman berdasarkan filter aktif
.elementElementElemen container wrapper

JavaScript — Custom Events

EventDetailKeterangan
fondasi:table:sort{col, dir, table}Dipanggil setelah kolom diurutkan
fondasi:table:select{selected, row, table}Dipanggil saat baris dipilih/dibatalkan
fondasi:table:page{page, pageSize, table}Dipanggil saat halaman berpindah
fondasi:table:search{query, table}Dipanggil setelah search dijalankan
fondasi:table:bulk{action, selected, table}Dipanggil saat tombol bulk action diklik

JavaScript — Inisialisasi

import { DataTable, getTable } from 'fondasi';

// ── Cara 1: Constructor langsung ─────────────────────────────
const table = new DataTable(document.getElementById('myWrap'), {
    sortable:      true,
    selectable:    true,
    paginate:      true,
    pageSize:      10,
    expandable:    true,
    searchInput:   document.getElementById('mySearch'),
    pageSizeSelect: document.getElementById('myPageSize'),
    sortCol:       'nama',
    sortDir:       'asc',
    emptyMessage:  'Belum ada data tersedia.',
    emptyIcon:     '📭',

    onBulk: ({ action, selected }) => {
        if (action === 'export') table.exportCSV('data.csv');
        if (action === 'delete') console.log('hapus', selected.length, 'baris');
    },
    onSearch: ({ query }) => console.log('search:', query),
    onSelect: ({ selected }) => console.log(selected.length, 'dipilih'),
    onPage:   ({ page }) => console.log('halaman:', page),
});

// ── Cara 2: getTable (singleton per elemen) ──────────────────
const table = getTable('#myWrap', { sortable: true });

// ── Cara 3: Deklaratif via data-attribute ────────────────────
// Cukup tambahkan data-table + data-* pada wrapper di HTML
// lalu panggil Fondasi.initComponents() sekali saja

// ── Contoh penggunaan API ────────────────────────────────────
table.search('andi');               // filter langsung
table.sort('tanggal', 'desc');      // urutkan kolom
table.goTo(2);                      // ke halaman 2
table.selectAll();                  // pilih semua baris tampil
table.exportCSV('laporan.csv');     // unduh CSV
table.setLoading(true, 5);          // tampilkan 5 skeleton baris

// Dengarkan custom event
document.getElementById('myWrap').addEventListener('fondasi:table:select', e => {
    console.log(e.detail.selected.length, 'baris dipilih');
});

Skeleton Loading

Nama Email Status
// Tampilkan skeleton saat fetch data
table.setLoading(true, 5);     // 5 baris skeleton

// Setelah data selesai dimuat:
table.setRows(newRows);        // isi data baru
// atau:
table.setLoading(false);       // restore baris sebelumnya

Empty State

NamaEmailStatus
📭
Tidak ada data.
Tidak ditemukan hasil untuk "kunci pencarian"
<!-- Ditambahkan otomatis oleh DataTable JS -->
<tr class="tr-empty">
    <td colspan="N">
        <span class="empty-icon">📭</span>
        <div class="empty-title">Tidak ada data.</div>
        <div class="empty-desc">Tidak ditemukan hasil untuk "<em>query</em>"</div>
    </td>
</tr>

Tabs

Komponen tabs untuk navigasi konten bertingkat dengan berbagai variant.

Class Reference

Class/Data Attribute Description
.tabsContainer utama tabs
.tabs-listContainer untuk tab buttons
.tabTab button
.tab-activeState tab yang aktif
.tab-panelKonten panel
.tabs-pillsVariant pills
.tabs-boxedVariant boxed
.tabs-underlineVariant underline
.tabs-smSize small
.tabs-lgSize large
.tabs-fitTab dengan lebar penuh
.tabs-centeredTab rata tengah

Default Tabs (Underline)

Dashboard content - Overview of your application metrics and key insights.

<div class="tabs">
    <div class="tabs-list">
        <button class="tab tab-active">Tab 1</button>
        <button class="tab">Tab 2</button>
    </div>
    <div class="tab-panel tab-panel-show">Content 1</div>
</div>

Tabs: Pills

<div class="tabs tabs-pills">
    <div class="tabs-list">
        <button class="tab tab-active">Active</button>
        <button class="tab">Inactive</button>
        <button class="tab">Draft</button>
    </div>
</div>

Tabs: Boxed

<div class="tabs tabs-boxed">
    <div class="tabs-list">
        <button class="tab tab-active">Overview</button>
        <button class="tab">Details</button>
        <button class="tab">History</button>
    </div>
</div>

Tabs: Small

<div class="tabs tabs-sm">
    <div class="tabs-list">
        <button class="tab tab-active">Small</button>
        <button class="tab">Tab</button>
    </div>
</div>

Tabs: Large

<div class="tabs tabs-lg">
    <div class="tabs-list">
        <button class="tab tab-active">Large</button>
        <button class="tab">Tab</button>
    </div>
</div>

Tabs: Fit (Full Width)

<div class="tabs tabs-fit">
    <div class="tabs-list">
        <button class="tab tab-active">Tab 1</button>
        <button class="tab">Tab 2</button>
        <button class="tab">Tab 3</button>
        <button class="tab">Tab 4</button>
    </div>
</div>

JavaScript Usage

import { Tabs } from 'fondasi';

const tabs = new Tabs('[data-nusa-tabs]', {
    activeClass: 'tab-active',
    onChange: (tabId) => { console.log(tabId); }
});

Accordion

Komponen accordion untuk konten yang dapat dilipat.

Class Reference

Class/Data Attribute Description
data-nusa-accordionMarks container as accordion
.accordion-itemIndividual accordion item
.accordion-headerClickable header toggle
.accordion-contentCollapsible content
data-accordion-toggleMarks header as toggle

Default Accordion

This is the content for the first accordion item. It can contain any HTML elements.

<div class="accordion" data-nusa-accordion>
    <div class="accordion-item">
        <button class="accordion-header" data-accordion-toggle>
            <span>Item 1</span>
            <span class="accordion-icon">+</span>
        </button>
        <div class="accordion-content">Content</div>
    </div>
</div>

JavaScript Usage

import { Accordion } from 'fondasi';

const accordion = new Accordion('[data-nusa-accordion]', {
    multiple: false,
    animated: true
});

Drawer

Komponen drawer untuk panel slide-in dari sisi.

Class Reference

Class Description
.drawerDrawer container
.drawer-panelSlide-in panel
.drawer-headerHeader section
.drawer-bodyBody content
data-drawer-closeCloses drawer on click
data-drawer-openOpens drawer on click

Drawer: Left

Left Drawer

This drawer slides in from the left side.

<!-- Trigger -->
<button data-drawer-open="my-drawer">Open Drawer</button>

<!-- Drawer -->
<div class="drawer" id="my-drawer">
    <div class="drawer-backdrop"></div>
    <div class="drawer-panel">
        <div class="drawer-header">
            <h3>Drawer Title</h3>
            <button data-drawer-close>×</button>
        </div>
        <div class="drawer-body">Content</div>
    </div>
</div>

Drawer: Right

Right Drawer

This drawer slides in from the right side.

JavaScript Usage

import { Drawer, getDrawer } from 'fondasi';

const drawer = getDrawer('#my-drawer');
drawer.open();
drawer.close();

Topbar

Komponen header bar horizontal yang menampilkan aksi-aksi utama di bagian atas halaman. Biasanya digunakan bersama Sidebar untuk membangun layout aplikasi (app shell).

Class Reference

ClassDescription
.topbarContainer utama topbar (height 60px)
.topbar-leftBagian kiri — flex, isi breadcrumb / mobile toggle
.topbar-centerBagian tengah — rata tengah
.topbar-rightBagian kanan — flex, isi icon buttons
.topbar-titleJudul halaman dalam topbar
.topbar-btnTombol ikon 36×36px di dalam topbar
.topbar-badgeDot notifikasi di sudut tombar-btn
.topbar-badge-countBadge angka notifikasi di sudut topbar-btn
.topbar-dividerPemisah vertikal antar section
.topbar-stickyModifier: sticky ke atas layar
.topbar-elevatedModifier: shadow, tanpa border bawah
.topbar-darkModifier: latar belakang gelap (brand-darker)

Default Topbar

Dashboard
<header class="topbar topbar-sticky">
    <div class="topbar-left">
        <span class="topbar-title">Dashboard</span>
    </div>
    <div class="topbar-right">
        <button class="topbar-btn">...</button>
    </div>
</header>

Topbar dengan Badge Notifikasi

Inbox
<button class="topbar-btn">
    <!-- icon SVG -->
    <span class="topbar-badge"></span>        <!-- dot merah -->
    <span class="topbar-badge-count">3</span> <!-- angka -->
</button>
<div class="topbar-divider"></div>

Topbar Dark

Admin Panel
<header class="topbar topbar-sticky topbar-dark">
    ...
</header>

Toast

Notifikasi popup yang muncul dari pojok layar. Formal, minimalist, dan menarik.

Interactive Demo

Klik tombol di bawah untuk menampilkan toast di pojok kanan bawah:

Toast with Actions

Class Reference

Class Description
.toast-containerContainer untuk toast
.toastToast element
.toast-successSuccess variant
.toast-warningWarning variant
.toast-dangerError/Danger variant
.toast-infoInfo variant
.toast-icon-wrapperIcon container dengan background
.toast-contentContent wrapper
.toast-titleJudul toast
.toast-messagePesan detail
.toast-actionsContainer untuk action buttons
.toast-progressProgress bar untuk durasi

HTML Structure

<div class="toast toast-success">
    <div class="toast-icon-wrapper">
        <svg class="toast-icon">...</svg>
    </div>
    <div class="toast-content">
        <div class="toast-title">Success!</div>
        <div class="toast-message">Your changes have been saved.</div>
    </div>
    <button class="toast-close">...</button>
</div>

JavaScript Usage

import { getToast } from 'fondasi';

const toast = getToast();

// Basic toast
toast.show('Message text', 'success');

// With title
toast.show('Operation completed', 'success', {
    title: 'Success'
});

// With actions
toast.show('File uploaded successfully', 'success', {
    actions: [
        { label: 'View', onClick: () => console.log('view') },
        { label: 'Undo', onClick: () => console.log('undo') }
    ]
});

// Auto dismiss with duration
toast.show('Message', 'info', { duration: 5000 });

Avatar

Komponen avatar untuk menampilkan representasi pengguna dengan berbagai ukuran, bentuk, dan status.

Class Reference

Class Description
.avatarDefault avatar
.avatar-smSmall size (24px)
.avatar-mdMedium size (32px)
.avatar-lgLarge size (40px)
.avatar-xlExtra large (56px)
.avatar-squareSquare shape
.avatar-circleCircle shape
.avatar-roundedRounded corners
.avatar-statusAvatar with status indicator
.avatar-status-onlineOnline status
.avatar-status-offlineOffline status
.avatar-status-busyBusy status
.avatar-status-awayAway status
.avatar-brandBrand color variant
.avatar-successSuccess color variant
.avatar-warningWarning color variant
.avatar-dangerDanger color variant
.avatar-infoInfo color variant
.avatar-groupAvatar group container
.avatar-group-smSmall group
.avatar-group-lgLarge group
.avatar-group-xlExtra large group
.avatar-initialText initial display

Sizes

<div class="avatar avatar-sm" data-initial="SM"></div>
<div class="avatar avatar-md" data-initial="MD"></div>
<div class="avatar avatar-lg" data-initial="LG"></div>
<div class="avatar avatar-xl" data-initial="XL"></div>

Shapes

<div class="avatar avatar-md" data-initial="SQ"></div>
<div class="avatar avatar-md avatar-circle" data-initial="CI"></div>
<div class="avatar avatar-md avatar-rounded" data-initial="RD"></div>

With Image Source

<div class="avatar avatar-md avatar-circle" data-src="https://i.pravatar.cc/150?img=68"></div>

With Initials

<div class="avatar avatar-md" data-initial="JD"></div>
<div class="avatar avatar-md avatar-circle" data-initial="AB"></div>
<div class="avatar avatar-md avatar-rounded" data-initial="XY"></div>

Status Indicator

<div class="avatar avatar-md avatar-circle avatar-status avatar-status-online" data-initial="ON"></div>
<div class="avatar avatar-md avatar-circle avatar-status avatar-status-offline" data-initial="OF"></div>
<div class="avatar avatar-md avatar-circle avatar-status avatar-status-busy" data-initial="BS"></div>
<div class="avatar avatar-md avatar-circle avatar-status avatar-status-away" data-initial="AW"></div>

Color Variants

<div class="avatar avatar-md" data-initial="B"></div>
<div class="avatar avatar-md avatar-circle" data-initial="S"></div>
<div class="avatar avatar-md avatar-brand" data-initial="B"></div>
<div class="avatar avatar-md avatar-circle avatar-brand" data-initial="B"></div>
<div class="avatar avatar-md avatar-success" data-initial="S"></div>
<div class="avatar avatar-md avatar-warning" data-initial="W"></div>
<div class="avatar avatar-md avatar-danger" data-initial="D"></div>
<div class="avatar avatar-md avatar-info" data-initial="I"></div>

Fallback: Icon (No image, no initial)

<div class="avatar avatar-sm"></div>
<div class="avatar avatar-md"></div>
<div class="avatar avatar-lg"></div>
<div class="avatar avatar-md avatar-circle"></div>
<div class="avatar avatar-md avatar-rounded"></div>

Avatar Group (Stack)

<div class="avatar-group">
    <div class="avatar avatar-md avatar-circle" data-initial="JD"></div>
    <div class="avatar avatar-md avatar-circle" data-initial="AB"></div>
    <div class="avatar avatar-md avatar-circle" data-initial="CD"></div>
    <div class="avatar avatar-md avatar-circle" data-initial="EF"></div>
</div>

Avatar Group with Overflow

<div class="avatar-group" data-max="3">
    <div class="avatar avatar-md avatar-circle" data-initial="A"></div>
    <div class="avatar avatar-md avatar-circle" data-initial="B"></div>
    <div class="avatar avatar-md avatar-circle" data-initial="C"></div>
    <div class="avatar avatar-md avatar-circle" data-initial="D"></div>
    <div class="avatar avatar-md avatar-circle" data-initial="E"></div>
    <div class="avatar avatar-md avatar-circle" data-initial="F"></div>
</div>

Avatar Group Sizes

<div class="avatar-group avatar-group-sm">...</div>
<div class="avatar-group avatar-group-md">...</div>
<div class="avatar-group avatar-group-lg">...</div>
<div class="avatar-group avatar-group-xl">...</div>

Avatar with Images and Status

<div class="avatar-group">
    <div class="avatar avatar-md avatar-circle avatar-status avatar-status-online" data-src="..."></div>
    <div class="avatar avatar-md avatar-circle avatar-status avatar-status-offline" data-src="..."></div>
    <div class="avatar avatar-md avatar-circle avatar-status avatar-status-busy" data-initial="JD"></div>
    <div class="avatar avatar-md avatar-circle avatar-status avatar-status-away" data-initial="AB"></div>
</div>

JavaScript API

Create Avatar Programmatically

import { createAvatar, createAvatarGroup } from 'fondasi';

// Create single avatar
const avatar = createAvatar({
    src: 'https://example.com/photo.jpg',
    initial: 'JD',
    size: 'md',
    shape: 'circle',
    status: 'online',
    variant: 'brand'
});

// Create avatar group
const group = createAvatarGroup([
    { initial: 'JD', status: 'online' },
    { initial: 'AB', status: 'offline' },
    { src: 'https://example.com/photo.jpg' }
], { size: 'md', max: 3 });

Events

Event Description
avatar:loadFired when image loads successfully
avatar:errorFired when image fails to load
document.querySelector('.avatar').addEventListener('avatar:load', (e) => {
    console.log('Image loaded:', e.detail.src);
});

document.querySelector('.avatar').addEventListener('avatar:error', (e) => {
    console.log('Image failed:', e.detail.src);
});

Layout System

Sistem layout dasar menggunakan container dan spacing.

Container

<div class="container">
    <!-- content -->
</div>

Width

Class Description
.w-fullwidth: 100%
.w-autowidth: auto
.w-screenwidth: 100vw
.w-minwidth: min-content
.w-maxwidth: max-content

Height

Class Description
.h-fullheight: 100%
.h-autoheight: auto
.h-screenheight: 100vh
.h-minheight: min-content
.h-maxheight: max-content

Flex

Utility classes untuk flexbox layout.

Display

<div class="flex">...</div>
<div class="inline-flex">...</div>

Flex Direction

Class CSS
.flex-rowflex-direction: row
.flex-row-reverseflex-direction: row-reverse
.flex-colflex-direction: column
.flex-col-reverseflex-direction: column-reverse

Justify Content

Class CSS
.justify-startjustify-content: flex-start
.justify-endjustify-content: flex-end
.justify-centerjustify-content: center
.justify-betweenjustify-content: space-between
.justify-aroundjustify-content: space-around

Align Items

Class CSS
.items-startalign-items: flex-start
.items-endalign-items: flex-end
.items-centeralign-items: center
.items-stretchalign-items: stretch

Gap

<div class="flex gap-2">...</div>
<div class="flex gap-4 gap-6-md">...</div>

Grid

Utility classes untuk CSS Grid layout.

Grid Columns

<div class="grid grid-cols-2">...</div>
<div class="grid grid-cols-3">...</div>
<div class="grid grid-cols-4">...</div>
Class Columns
.grid-cols-11 column
.grid-cols-22 columns
.grid-cols-33 columns
.grid-cols-44 columns
.grid-cols-66 columns
.grid-cols-1212 columns

Grid Span

<div class="grid-col-span-2">...</div>
<div class="grid-col-span-3">...</div>

Utility Classes

Kumpulan utility classes yang tersedia.

Display

Class CSS
.blockdisplay: block
.inline-blockdisplay: inline-block
.inlinedisplay: inline
.hiddendisplay: none

Position

Class CSS
.relativeposition: relative
.absoluteposition: absolute
.fixedposition: fixed
.stickyposition: sticky

Border Radius

Class CSS
.rounded-noneborder-radius: 0
.roundedborder-radius: var(--radius-md)
.rounded-lgborder-radius: var(--radius-lg)
.rounded-fullborder-radius: 9999px

Shadow

Class Description
.shadow-noneNo shadow
.shadow-smSmall shadow
.shadowDefault shadow
.shadow-lgLarge shadow

Transitions

Class CSS
.transition-nonetransition: none
.transition-alltransition: all var(--transition-base)
.transition-colorstransition for colors
.transition-opacitytransition for opacity
.transition-transformtransition for transform

Sizing

Utility classes untuk mengatur width dan height.

Width

<div class="w-1/2">50%</div>
<div class="w-1/3">33.33%</div>
<div class="w-3/4">75%</div>
<div class="w-auto">auto</div>

Height

<div class="h-screen">100vh</div>
<div class="h-auto">auto</div>
<div class="h-full">100%</div>

Min/Max

<div class="min-h-screen">min-height: 100vh</div>
<div class="max-w-md">max-width: 28rem</div>
<div class="max-w-lg">max-width: 32rem</div>

Interaction

Utility classes untuk interaksi pengguna.

Cursor

Class CSS
.cursor-pointercursor: pointer
.cursor-defaultcursor: default
.cursor-not-allowedcursor: not-allowed
.cursor-movecursor: move

User Select

Class CSS
.select-noneuser-select: none
.select-textuser-select: text
.select-alluser-select: all

Pointer Events

Class CSS
.pointer-events-nonepointer-events: none
.pointer-events-autopointer-events: auto

JavaScript API

Referensi lengkap API JavaScript Fondasi.

Inisialisasi

// ES Modules
import { initComponents } from 'fondasi';

initComponents(); // Initialize all components

// UMD / Browser
Fondasi.initComponents();

Available Exports

Export Description
initComponentsInisialisasi semua komponen
ModalModal component (open/close)
DrawerDrawer component (open/close)
TabsTabs component
AccordionAccordion component
DropdownDropdown component
SidebarSidebar component
DataTableTable component
getToastToast manager
themeTheme manager (light/dark)
CharCounterCharacter counter
PasswordTogglePassword visibility toggle
SearchSelectSearchable select
FormValidationForm validation

Theme

import { theme } from 'fondasi';

// Toggle dark mode
theme.toggle();

// Set specific mode
theme.setMode('dark');
theme.setMode('light');

// Check current mode
theme.getMode(); // 'light' or 'dark'

Template — Dashboard

Template siap pakai untuk membangun halaman dashboard dalam portal SIMPATTI. Setiap aplikasi portal cukup menyalin template ini dan melakukan konfigurasi minimal di APP_CONFIG.

Preview Template

Gambaran Umum

Template Dashboard dirancang untuk portal multi-aplikasi di mana setiap aplikasi memiliki:

  • Layout yang seragam — Sidebar + Topbar + Main Content
  • Warna/tema yang berbeda per aplikasi, cukup set satu warna dasar
  • Menu sidebar dan fitur yang dapat dikustomisasi sepenuhnya

Struktur File Aplikasi

Setiap aplikasi dalam portal memiliki foldernya sendiri dengan tiga file utama:

portal/
├── shared/
│   ├── fondasi.css      ← dari CDN atau shared lib
│   └── fondasi.js
│
└── apps/
    ├── akademik/
    │   ├── index.html   ← salin dari template dashboard
    │   ├── app.css      ← override/tambahan CSS khusus app
    │   └── app.js       ← logika bisnis khusus app
    │
    ├── keuangan/
    │   ├── index.html
    │   ├── app.css
    │   └── app.js
    │
    └── [nama-app]/
        ├── index.html
        ├── app.css
        └── app.js

Quick Start

Langkah membuat aplikasi baru dari template ini:

  1. Unduh dan salin folder src/templates/dashboard/ ke direktori aplikasi baru
  2. Ganti URL Fondasi (CSS & JS) dengan CDN URL yang sesuai di index.html
  3. Edit APP_CONFIG di bagian atas app.js
  4. Sesuaikan menu sidebar di index.html dan tambahkan logika bisnis di app.js

APP_CONFIG

Satu-satunya titik konfigurasi wajib per aplikasi. Letakkan di bagian atas blok <script>.

// Nilai default dalam template (app.js)
const APP_CONFIG = {
  name:      'Dashboard',  // Ganti dengan nama aplikasi
  baseColor: '#1e3a5f',             // Ganti dengan warna dasar brand aplikasi
  user: {
    name:     '[Nama Pengguna]',    // Isi dari session / auth
    role:     '[Peran / Jabatan]',
    initials: 'AD',                 // 2 huruf untuk avatar
  },
};

// Contoh setelah dikustomisasi untuk aplikasi Akademik:
// const APP_CONFIG = {
//   name:      'Akademik',
//   baseColor: '#1e6b3f',
//   user: { name: 'Andi Nugroho', role: 'Administrator', initials: 'AN' },
// };
KeyTipeKeterangan
namestringNama aplikasi. Muncul di <title> dan brand sidebar.
baseColorhex stringWarna dasar brand. Diproses oleh setAppTheme() untuk generate semua token warna.
user.namestringNama pengguna aktif — isi dari session/auth aplikasi.
user.rolestringPeran atau jabatan pengguna.
user.initialsstringDua huruf untuk avatar (misalnya "AN" dari "Andi Nugroho").

setAppTheme(baseColor)

Fungsi ini menerima satu warna hex dan secara otomatis menghasilkan seluruh skala warna brand. Token yang di-generate langsung menggantikan variabel CSS Fondasi, sehingga semua komponen (button, form, tabs, checkbox, sidebar, dll.) ikut menyesuaikan warna secara otomatis.

// Contoh penggunaan langsung
setAppTheme('#1e6b3f');   // Hijau — untuk app Akademik
setAppTheme('#7c3aed');   // Ungu — untuk app Kurikulum
setAppTheme('#b45309');   // Amber — untuk app Keuangan

Token CSS yang di-generate:

TokenKeterangan
--color-brandWarna dasar (input langsung)
--color-brand-defaultSama dengan base — token utama yang dipakai seluruh komponen Fondasi
--color-brand-dark10% lebih gelap — hover state button & link
--color-brand-darker20% lebih gelap — active/pressed state
--color-brand-lightVersi sangat terang — background highlight & focus ring
--color-brand-midVersi medium — border & divider bernuansa brand
--color-brand-accentSedikit lebih cerah — CTA dan fokus
--color-brand-rgbNilai RGB untuk dipakai di rgba()

Layout Struktur

Template menggunakan pola Sidebar Fixed + Topbar Sticky + Main Content.

<body>
  <!-- Overlay mobile (tampil saat sidebar terbuka di mobile) -->
  <div class="sidebar-overlay" id="sidebarOverlay"></div>

  <!-- Sidebar (fixed kiri, light mode) -->
  <aside class="sidebar" id="mainSidebar">
    <div class="sidebar-header">...</div>   <!-- Logo + toggle -->
    <div class="sidebar-content">...</div>  <!-- Nav sections -->
    <div class="sidebar-footer">...</div>   <!-- Avatar + Profil + Keluar -->
  </aside>

  <!-- Wrapper utama (margin-left mengikuti lebar sidebar) -->
  <div class="app-wrapper" id="appWrapper">

    <!-- Topbar (sticky top) -->
    <header class="topbar">
      <div class="topbar-left">...</div>   <!-- Mobile toggle + Breadcrumb -->
      <div class="topbar-right">...</div>  <!-- Notifikasi, user -->
    </header>

    <!-- Konten halaman -->
    <main class="main-content">
      ...
    </main>

  </div>

  <!-- Modal, Drawer di sini -->
</body>

CSS Layout Classes

Kelas-kelas berikut adalah bagian dari app shell yang harus didefinisikan di app.css atau di blok <style> dalam template (bukan bagian dari Fondasi):

ClassKeterangan
.app-wrapperWrapper utama. margin-left: 260px mengikuti lebar sidebar.
.app-wrapper.sidebar-is-collapsedDiterapkan oleh JS saat sidebar collapsed. Ubah margin menjadi 68px.
.topbarHeader sticky dengan tinggi 60px.
.main-contentArea konten utama dengan padding var(--space-6).

Komponen Fondasi yang Dipakai

KomponenLetak dalam Template
SidebarNavigasi utama aplikasi (kiri, light mode)
DropdownMenu notifikasi dan menu user (dengan data-placement="bottom-end")
BreadcrumbNavigasi hierarki di topbar
CardStat cards & container konten utama
TabsSegmentasi data (Semua / Aktif / Arsip)
TableTampilan data dengan aksi per baris
BadgeIndikator status (Aktif, Pending, Nonaktif)
ButtonAksi primer, outline, ghost, dan danger
FormInput, select, textarea, checkbox di filter & modal
ModalDetail data & form tambah/edit
DrawerPanel filter dari kanan layar
ToastFeedback aksi (sukses, error, info, warning)
AvatarInisial pengguna di sidebar footer & topbar

Inisialisasi JavaScript

Template menginisialisasi semua komponen Fondasi melalui UMD global Fondasi yang tersedia setelah fondasi.js dimuat.

// app.js — inisialisasi komponen di DOMContentLoaded
document.addEventListener('DOMContentLoaded', function () {

  // 1. Terapkan tema warna aplikasi
  setAppTheme(APP_CONFIG.baseColor);

  // 2. Sidebar — sinkronkan margin wrapper saat collapse/expand
  const sidebar = new Fondasi.Sidebar(document.getElementById('mainSidebar'), {
    onChange: ({ isCollapsed }) => {
      document.getElementById('appWrapper')
        .classList.toggle('sidebar-is-collapsed', isCollapsed);
    },
  });

  // 3. Tabs
  const tabs = new Fondasi.Tabs(document.getElementById('mainTabs'));

  // 4. Drawer (filter) dan Modal (detail + form)
  const filterDrawer = new Fondasi.Drawer(document.getElementById('filterDrawer'));
  const detailModal  = new Fondasi.Modal(document.getElementById('detailModal'));
  const formModal    = new Fondasi.Modal(document.getElementById('formModal'));

  // 5. Toast
  const toast = new Fondasi.ToastManager({ position: 'top-right' });

  // 6. Semua dropdown (notifikasi, user)
  document.querySelectorAll('.dropdown').forEach(el => new Fondasi.Dropdown(el));

  // 7. Mobile sidebar toggle
  const mobileSidebarBtn = document.getElementById('mobileSidebarToggle');
  const sidebarOverlay   = document.getElementById('sidebarOverlay');
  if (mobileSidebarBtn) {
    mobileSidebarBtn.addEventListener('click', () => {
      document.getElementById('mainSidebar').classList.toggle('sidebar-open');
      sidebarOverlay.classList.toggle('sidebar-overlay-open');
    });
  }

});

Kustomisasi Sidebar

Ganti placeholder menu di dalam .sidebar-content sesuai fitur aplikasi. Template menyediakan tiga section: Utama, Manajemen, dan Sistem.

<!-- Tambah menu baru -->
<a href="halaman.html" class="sidebar-link">
  <svg class="sidebar-nav-icon" ...>...</svg>
  <span class="sidebar-nav-text">Nama Menu</span>
  <span class="sidebar-nav-badge">3</span>  <!-- opsional -->
</a>

<!-- Tandai menu aktif -->
<a href="halaman.html" class="sidebar-link active">...</a>

CDN URL

Template menggunakan path lokal (../../../dist/) untuk pengembangan dari lokasi src/templates/dashboard/. Ganti dengan CDN URL sebelum deploy:

<!-- Development (lokal) — dari src/templates/dashboard/ -->
<link rel="stylesheet" href="../../../dist/fondasi.css">
<script src="../../../dist/fondasi.js"></script>

<!-- Production (CDN) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/bideyesa/fondasi@latest/dist/fondasi.min.css">
<script src="https://cdn.jsdelivr.net/gh/bideyesa/fondasi@latest/dist/fondasi.min.js"></script>