Tani
Official Documentation for the Tani CSS Framework - A lightweight, utility-first CSS framework with rich component library
Introduction
Tani is a modern CSS framework that provides a comprehensive set of utility classes and components to build responsive, beautiful web interfaces quickly and efficiently.
Features
- Utility-first approach for rapid development
- Rich component library (buttons, cards, badges, navbar, etc.)
- Comprehensive color palette
- Extensive utility classes for spacing, typography, and more
- No JavaScript dependencies
- Lightweight and performant
Browser Support
Tani supports all modern browsers:
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Installation
-
Download
Download the CSS file and include it in your project:
<link rel="stylesheet" href="./dist/css/tani.css">
Color Palette
Tani comes with a carefully crafted color palette designed for accessibility and visual appeal.
Badges
Badges are small status indicators useful for highlighting information.
Standard Badges
<span class="badge badge-primary">Primary</span>
<span class="badge badge-secondary">Secondary</span>
<span class="badge badge-success">Success</span>
<span class="badge badge-danger">Danger</span>
<span class="badge badge-warning">Warning</span>
<span class="badge badge-dark">Dark</span>
Badge Sizes
<span class="badge badge-primary badge-sm">Small</span>
<span class="badge badge-primary">Normal</span>
<span class="badge badge-primary badge-lg">Large</span>
Pill Badges
<span class="badge badge-primary badge-pill">Primary</span>
<span class="badge badge-secondary badge-pill">Secondary</span>
<span class="badge badge-success badge-pill">Success</span>
Cards
Cards are flexible content containers for displaying information.
Basic Card
Card Title
Some quick example text to build on the card title and make up the bulk of the card's content.
Go somewhere<div class="card">
<div class="card-body">
<h5 class="card-title">Card Title</h5>
<p class="card-text">Some quick example text...</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
</div>
Card with Header and Footer
Special title treatment
With supporting text below as a natural lead-in to additional content.
Go somewhere<div class="card">
<div class="card-header">
Featured
</div>
<div class="card-body">
<h5 class="card-title">Special title treatment</h5>
<p class="card-text">With supporting text...</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-muted">
2 days ago
</div>
</div>
Colored Cards
Primary card title
Some quick example text to build on the card title.
<div class="card card-primary">
<div class="card-header">Primary Card</div>
<div class="card-body">
<h5 class="card-title">Primary card title</h5>
<p class="card-text">Some quick example text...</p>
</div>
</div>
Card Sizes
Small Card
This is a small card.
Large Card
This is a large card with more padding.
<div class="card card-sm">
<div class="card-body">
<h5 class="card-title">Small Card</h5>
<p class="card-text">This is a small card.</p>
</div>
</div>
<div class="card card-lg">
<div class="card-body">
<h5 class="card-title">Large Card</h5>
<p class="card-text">This is a large card with more padding.</p>
</div>
</div>
Dropdowns
Dropdowns are toggleable overlays for displaying lists of links, actions, and more. They work great as standalone components or within navigation bars.
Basic Dropdown
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
Dropdown Button
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</div>
Dropdown with Divider
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
Menu Options
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Regular link</a></li>
<li><a class="dropdown-item" href="#">Another link</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Separated link</a></li>
</ul>
</div>
Dropdown Button Variants
<div class="dropdown">
<button class="btn btn-success dropdown-toggle" type="button" data-toggle="dropdown">
Success
</button>
<ul class="dropdown-menu">...</ul>
</div>
<div class="dropdown">
<button class="btn btn-danger dropdown-toggle" type="button" data-toggle="dropdown">
Danger
</button>
<ul class="dropdown-menu">...</ul>
</div>
Dropdown with Active and Disabled Items
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
Dropdown States
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Regular item</a></li>
<li><a class="dropdown-item active" href="#">Active item</a></li>
<li><a class="dropdown-item disabled" href="#">Disabled item</a></li>
<li><a class="dropdown-item" href="#">Another item</a></li>
</ul>
</div>
Outline Button Dropdowns
<div class="dropdown">
<button class="btn btn-outline-primary dropdown-toggle" type="button" data-toggle="dropdown">
Primary
</button>
<ul class="dropdown-menu">...</ul>
</div>
Usage in Navigation
Dropdowns are commonly used within navigation bars. See the Navbar section for examples of dropdowns integrated into navigation menus.
JavaScript
Dropdowns require JavaScript to function. The dropdown toggle functionality is already included in the navbar JavaScript code. Here's a standalone version:
// Dropdown toggle functionality
const dropdownToggles = document.querySelectorAll('.dropdown-toggle, [data-toggle="dropdown"]');
dropdownToggles.forEach(toggle => {
toggle.addEventListener('click', function (e) {
e.preventDefault();
const dropdown = this.closest('.dropdown');
const menu = dropdown ? dropdown.querySelector('.dropdown-menu') : null;
if (menu) {
const isOpen = menu.classList.contains('show');
// Close all other dropdowns
document.querySelectorAll('.dropdown-menu.show').forEach(openMenu => {
if (openMenu !== menu) {
openMenu.classList.remove('show');
}
});
// Toggle current dropdown
menu.classList.toggle('show');
this.setAttribute('aria-expanded', !isOpen);
}
});
});
// Close dropdowns when clicking outside
document.addEventListener('click', function (event) {
if (!event.target.closest('.dropdown')) {
document.querySelectorAll('.dropdown-menu.show').forEach(menu => {
menu.classList.remove('show');
});
}
});
Modals
Modals are dialog boxes that overlay the main content to focus user attention on important information or actions.
Basic Modal
<button class="btn btn-primary" onclick="openModal('exampleModal')">Launch Modal</button>
<div id="exampleModal" class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal Title</h5>
<button type="button" class="modal-close" onclick="closeModal('exampleModal')">×</button>
</div>
<div class="modal-body">
<p>This is a basic modal dialog. You can put any content here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="closeModal('exampleModal')">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
Modal Sizes
<!-- Small Modal -->
<div class="modal-dialog modal-sm">...</div>
<!-- Large Modal -->
<div class="modal-dialog modal-lg">...</div>
<!-- Extra Large Modal -->
<div class="modal-dialog modal-xl">...</div>
Centered Modal
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
...
</div>
</div>
Animations
Modals include smooth fade-in and slide-down animations by default. The backdrop fades in while the modal content slides down from above with a smooth opacity transition.
- Backdrop fade: 0.3s ease transition
- Modal slide-down: 50px translateY with opacity fade
- Box shadow for depth perception
- Smooth closing animation with fade-out
JavaScript
Modals require JavaScript to function. Here's the complete code with animation support:
// Open modal with animation
function openModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'block';
modal.classList.remove('fade-out');
modal.offsetHeight; // Trigger reflow
modal.classList.add('show');
document.body.style.overflow = 'hidden';
}
}
// Close modal with animation
function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('fade-out');
modal.classList.remove('show');
setTimeout(() => {
if (!modal.classList.contains('show')) {
modal.style.display = 'none';
modal.classList.remove('fade-out');
}
}, 300);
document.body.style.overflow = '';
}
}
// Close modal when clicking outside
document.addEventListener('click', function(event) {
if (event.target.classList.contains('modal')) {
closeModal(event.target.id);
}
});
Toasts
Toasts are lightweight notifications designed to mimic push notifications.
Basic Toast
<div class="toast-container top-right">
<div class="toast" id="basicToast">
<div class="toast-header">
<strong>Notification</strong>
<small>just now</small>
<button class="toast-close" onclick="hideToast('basicToast')">×</button>
</div>
<div class="toast-body">
This is a basic toast notification message.
</div>
</div>
</div>
Toast Variants
<!-- Primary Toast -->
<div class="toast toast-primary">
<div class="toast-header">
<strong>Primary</strong>
<small>just now</small>
<button class="toast-close">×</button>
</div>
<div class="toast-body">
This is a primary toast notification.
</div>
</div>
<!-- Success Toast -->
<div class="toast toast-success">
<div class="toast-header">
<strong>Success</strong>
<small>just now</small>
<button class="toast-close">×</button>
</div>
<div class="toast-body">
Operation completed successfully!
</div>
</div>
<!-- Danger Toast -->
<div class="toast toast-danger">
<div class="toast-header">
<strong>Error</strong>
<small>just now</small>
<button class="toast-close">×</button>
</div>
<div class="toast-body">
An error occurred!
</div>
</div>
Toast Positions
<!-- Top Right -->
<div class="toast-container top-right">
<div class="toast">...</div>
</div>
<!-- Top Left -->
<div class="toast-container top-left">
<div class="toast">...</div>
</div>
<!-- Top Center -->
<div class="toast-container top-center">
<div class="toast">...</div>
</div>
<!-- Bottom Right -->
<div class="toast-container bottom-right">
<div class="toast">...</div>
</div>
<!-- Bottom Left -->
<div class="toast-container bottom-left">
<div class="toast">...</div>
</div>
<!-- Bottom Center -->
<div class="toast-container bottom-center">
<div class="toast">...</div>
</div>
Toast without Header
<div class="toast">
<div class="toast-body">
Simple toast message without header.
</div>
</div>
JavaScript
Toasts require JavaScript to show and hide. Here's the complete code:
// Toast functionality
function showToast(toastId, duration = 5000) {
const toast = document.getElementById(toastId);
if (!toast) return;
toast.classList.add('show');
toast.classList.remove('hiding');
// Auto hide after duration
if (duration > 0) {
setTimeout(() => {
hideToast(toastId);
}, duration);
}
}
function hideToast(toastId) {
const toast = document.getElementById(toastId);
if (!toast) return;
toast.classList.add('hiding');
toast.classList.remove('show');
// Remove hiding class after animation
setTimeout(() => {
toast.classList.remove('hiding');
}, 300);
}
// Create toast dynamically
function createToast(message, type = 'primary', position = 'top-right', duration = 5000) {
// Get or create container
let container = document.querySelector(`.toast-container.${position}`);
if (!container) {
container = document.createElement('div');
container.className = `toast-container ${position}`;
document.body.appendChild(container);
}
// Create toast
const toastId = 'toast-' + Date.now();
const toast = document.createElement('div');
toast.id = toastId;
toast.className = `toast toast-${type}`;
toast.innerHTML = `
<div class="toast-header">
<strong>${type.charAt(0).toUpperCase() + type.slice(1)}</strong>
<small>just now</small>
<button class="toast-close" onclick="hideToast('${toastId}')">×</button>
</div>
<div class="toast-body">${message}</div>
`;
container.appendChild(toast);
// Show toast
setTimeout(() => showToast(toastId, duration), 10);
// Remove from DOM after hiding
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, duration + 500);
}
createToast() to dynamically generate toasts, or define them in HTML and show/hide with showToast() and hideToast().
Progress
Progress bars are used to visualize the completion status of tasks or processes.
Basic Progress Bar
<div class="progress">
<div class="progress-bar" style="width: 25%"></div>
</div>
<div class="progress">
<div class="progress-bar" style="width: 50%"></div>
</div>
<div class="progress">
<div class="progress-bar" style="width: 75%"></div>
</div>
<div class="progress">
<div class="progress-bar" style="width: 100%"></div>
</div>
Progress Bar with Label
<div class="progress">
<div class="progress-bar" style="width: 25%">25%</div>
</div>
<div class="progress">
<div class="progress-bar" style="width: 60%">60%</div>
</div>
Colored Progress Bars
<div class="progress">
<div class="progress-bar progress-bar-primary" style="width: 25%">Primary</div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-secondary" style="width: 35%">Secondary</div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: 50%">Success</div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-danger" style="width: 65%">Danger</div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-warning" style="width: 75%">Warning</div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-info" style="width: 85%">Info</div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-dark" style="width: 95%">Dark</div>
</div>
Striped Progress Bars
<div class="progress">
<div class="progress-bar progress-bar-striped" style="width: 40%"></div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success progress-bar-striped" style="width: 60%"></div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-danger progress-bar-striped" style="width: 80%"></div>
</div>
Animated Progress Bars
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 45%"></div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success progress-bar-striped progress-bar-animated" style="width: 70%"></div>
</div>
Progress Bar Sizes
<!-- Small -->
<div class="progress progress-sm">
<div class="progress-bar" style="width: 50%"></div>
</div>
<!-- Normal -->
<div class="progress">
<div class="progress-bar" style="width: 50%"></div>
</div>
<!-- Large -->
<div class="progress progress-lg">
<div class="progress-bar" style="width: 50%">50%</div>
</div>
<!-- Extra Large -->
<div class="progress progress-xl">
<div class="progress-bar" style="width: 50%">50%</div>
</div>
Multiple Progress Bars
<div class="progress">
<div class="progress-bar progress-bar-primary" style="width: 15%">15%</div>
<div class="progress-bar progress-bar-success" style="width: 30%">30%</div>
<div class="progress-bar progress-bar-info" style="width: 20%">20%</div>
</div>
style="width: X%" attribute to set the progress value. You can dynamically update this with JavaScript to create animated progress indicators.
Dynamic Progress Example
function startProgress() {
const progressBar = document.getElementById('dynamicProgress');
let width = 0;
const interval = setInterval(() => {
if (width >= 100) {
clearInterval(interval);
} else {
width++;
progressBar.style.width = width + '%';
progressBar.textContent = width + '%';
}
}, 20);
}
function resetProgress() {
const progressBar = document.getElementById('dynamicProgress');
progressBar.style.width = '0%';
progressBar.textContent = '0%';
}
Spinners
Spinners are loading indicators that show content is being loaded or processed.
Border Spinner
<div class="spinner-border" role="status">
<span class="sr-only">Loading...</span>
</div>
Growing Spinner
<div class="spinner-grow" role="status">
<span class="sr-only">Loading...</span>
</div>
Spinner Colors
<div class="spinner-border spinner-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-border spinner-secondary" role="status">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-border spinner-success" role="status">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-border spinner-danger" role="status">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-border spinner-warning" role="status">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-border spinner-info" role="status">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-border spinner-dark" role="status">
<span class="sr-only">Loading...</span>
</div>
Growing Spinner Colors
<div class="spinner-grow spinner-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-grow spinner-secondary" role="status">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-grow spinner-success" role="status">
<span class="sr-only">Loading...</span>
</div>
Spinner Sizes
<!-- Small -->
<div class="spinner-border spinner-border-sm" role="status">
<span class="sr-only">Loading...</span>
</div>
<!-- Normal -->
<div class="spinner-border" role="status">
<span class="sr-only">Loading...</span>
</div>
<!-- Large -->
<div class="spinner-border spinner-border-lg" role="status">
<span class="sr-only">Loading...</span>
</div>
<!-- Extra Large -->
<div class="spinner-border spinner-border-xl" role="status">
<span class="sr-only">Loading...</span>
</div>
Growing Spinner Sizes
<!-- Small -->
<div class="spinner-grow spinner-grow-sm" role="status">
<span class="sr-only">Loading...</span>
</div>
<!-- Normal -->
<div class="spinner-grow" role="status">
<span class="sr-only">Loading...</span>
</div>
<!-- Large -->
<div class="spinner-grow spinner-grow-lg" role="status">
<span class="sr-only">Loading...</span>
</div>
<!-- Extra Large -->
<div class="spinner-grow spinner-grow-xl" role="status">
<span class="sr-only">Loading...</span>
</div>
Spinners in Buttons
<button class="btn btn-primary" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Loading...
</button>
<button class="btn btn-success" disabled>
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
Loading...
</button>
<button class="btn btn-secondary" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
</button>
<span class="sr-only">Loading...</span> for accessibility. This provides context for screen readers.
Forms
Tani provides a comprehensive set of form components for creating beautiful and functional forms.
Basic Form
<form>
<div class="form-group">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
<div class="form-text">We'll never share your email with anyone else.</div>
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="form-label">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1">
</div>
<div class="form-group">
<label for="exampleSelect" class="form-label">Select Example</label>
<select class="form-select" id="exampleSelect">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Check me out</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Form Controls
<div class="form-group">
<label for="exampleFormControlInput1" class="form-label">Email address</label>
<input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name@example.com">
</div>
<div class="form-group">
<label for="exampleFormControlTextarea1" class="form-label">Example textarea</label>
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea>
</div>
<div class="form-group">
<label for="formFile" class="form-label">Default file input example</label>
<input class="form-control" type="file" id="formFile">
</div>
Checkboxes and Radios
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault">
<label class="form-check-label" for="flexCheckDefault">
Default checkbox
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="flexCheckChecked" checked>
<label class="form-check-label" for="flexCheckChecked">
Checked checkbox
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="flexRadioDefault" id="flexRadioDefault1">
<label class="form-check-label" for="flexRadioDefault1">
Default radio
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="flexRadioDefault" id="flexRadioDefault2" checked>
<label class="form-check-label" for="flexRadioDefault2">
Default checked radio
</label>
</div>
Form Grid
<form>
<div class="form-row">
<div class="form-col">
<input type="text" class="form-control" placeholder="First name">
</div>
<div class="form-col">
<input type="text" class="form-control" placeholder="Last name">
</div>
</div>
</form>
Sizing
<input class="form-control form-control-lg" type="text" placeholder="Large input">
<input class="form-control" type="text" placeholder="Default input">
<input class="form-control form-control-sm" type="text" placeholder="Small input">
Pagination
Pagination components for navigating through pages of content.
Basic Pagination
<nav>
<ul class="pagination">
<li class="page-item">
<a class="page-link" href="#">Previous</a>
</li>
<li class="page-item">
<a class="page-link" href="#">1</a>
</li>
<li class="page-item active">
<a class="page-link" href="#">2</a>
</li>
<li class="page-item">
<a class="page-link" href="#">3</a>
</li>
<li class="page-item">
<a class="page-link" href="#">Next</a>
</li>
</ul>
</nav>
Pagination with Icons
<nav>
<ul class="pagination">
<li class="page-item">
<a class="page-link" href="#">ยซ</a>
</li>
<li class="page-item">
<a class="page-link" href="#">1</a>
</li>
<li class="page-item">
<a class="page-link" href="#">2</a>
</li>
<li class="page-item active">
<a class="page-link" href="#">3</a>
</li>
<li class="page-item">
<a class="page-link" href="#">4</a>
</li>
<li class="page-item">
<a class="page-link" href="#">5</a>
</li>
<li class="page-item">
<a class="page-link" href="#">ยป</a>
</li>
</ul>
</nav>
Disabled and Active States
<nav>
<ul class="pagination">
<li class="page-item disabled">
<a class="page-link" href="#">Previous</a>
</li>
<li class="page-item active">
<a class="page-link" href="#">1</a>
</li>
<li class="page-item">
<a class="page-link" href="#">2</a>
</li>
<li class="page-item">
<a class="page-link" href="#">3</a>
</li>
<li class="page-item">
<a class="page-link" href="#">Next</a>
</li>
</ul>
</nav>
Pagination Sizes
<!-- Small -->
<nav>
<ul class="pagination pagination-sm">
<li class="page-item">
<a class="page-link" href="#">ยซ</a>
</li>
<li class="page-item">
<a class="page-link" href="#">1</a>
</li>
<li class="page-item active">
<a class="page-link" href="#">2</a>
</li>
<li class="page-item">
<a class="page-link" href="#">3</a>
</li>
<li class="page-item">
<a class="page-link" href="#">ยป</a>
</li>
</ul>
</nav>
<!-- Normal -->
<nav>
<ul class="pagination">
...
</ul>
</nav>
<!-- Large -->
<nav>
<ul class="pagination pagination-lg">
...
</ul>
</nav>
Pagination Alignment
<!-- Left aligned (default) -->
<nav>
<ul class="pagination">
...
</ul>
</nav>
<!-- Center aligned -->
<nav>
<ul class="pagination pagination-center">
...
</ul>
</nav>
<!-- Right aligned -->
<nav>
<ul class="pagination pagination-end">
...
</ul>
</nav>
Rounded Pagination
<nav>
<ul class="pagination pagination-rounded">
<li class="page-item">
<a class="page-link" href="#">ยซ</a>
</li>
<li class="page-item">
<a class="page-link" href="#">1</a>
</li>
<li class="page-item">
<a class="page-link" href="#">2</a>
</li>
<li class="page-item active">
<a class="page-link" href="#">3</a>
</li>
<li class="page-item">
<a class="page-link" href="#">4</a>
</li>
<li class="page-item">
<a class="page-link" href="#">5</a>
</li>
<li class="page-item">
<a class="page-link" href="#">ยป</a>
</li>
</ul>
</nav>
active class on .page-item to indicate the current page, and disabled class to disable navigation links.
Accordion
Accordions allow users to toggle the display of sections of content.
Basic Accordion
<div class="accordion" id="accordionBasic">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-accordion="collapseOne">
Accordion Item #1
</button>
</h2>
<div id="collapseOne" class="accordion-collapse show">
<div class="accordion-body">
This is the first item's accordion body.
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-accordion="collapseTwo">
Accordion Item #2
</button>
</h2>
<div id="collapseTwo" class="accordion-collapse">
<div class="accordion-body">
This is the second item's accordion body.
</div>
</div>
</div>
</div>
Flush Accordion
<div class="accordion accordion-flush">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-accordion="flushOne">
Flush Item #1
</button>
</h2>
<div id="flushOne" class="accordion-collapse">
<div class="accordion-body">
Flush accordion removes borders and rounded corners.
</div>
</div>
</div>
</div>
Bordered Accordion
<div class="accordion accordion-bordered">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-accordion="borderedOne">
Bordered Item #1
</button>
</h2>
<div id="borderedOne" class="accordion-collapse">
<div class="accordion-body">
Each item has its own border and spacing.
</div>
</div>
</div>
</div>
Accordion with Icons
<div class="accordion accordion-icon">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-accordion="iconOne">
Icon Item #1
</button>
</h2>
<div id="iconOne" class="accordion-collapse">
<div class="accordion-body">
Accordion with plus/minus icon indicators.
</div>
</div>
</div>
</div>
Colored Accordions
<!-- Primary -->
<div class="accordion accordion-primary">...</div>
<!-- Secondary -->
<div class="accordion accordion-secondary">...</div>
<!-- Success -->
<div class="accordion accordion-success">...</div>
<!-- Dark -->
<div class="accordion accordion-dark">...</div>
Accordion with Shadow
<div class="accordion accordion-shadow">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-accordion="shadowOne">
Shadow Item #1
</button>
</h2>
<div id="shadowOne" class="accordion-collapse">
<div class="accordion-body">
Accordion with elevated shadow effect.
</div>
</div>
</div>
</div>
Combined Styles
<div class="accordion accordion-primary accordion-bordered accordion-shadow">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-accordion="combinedOne">
Combined Styles
</button>
</h2>
<div id="combinedOne" class="accordion-collapse">
<div class="accordion-body">
Primary + Bordered + Shadow combined together.
</div>
</div>
</div>
</div>
JavaScript
Accordions require JavaScript to toggle content. Here's the complete code:
// Accordion functionality
document.addEventListener('DOMContentLoaded', function() {
const accordionButtons = document.querySelectorAll('[data-accordion]');
accordionButtons.forEach(button => {
button.addEventListener('click', function() {
const targetId = this.getAttribute('data-accordion');
const target = document.getElementById(targetId);
const accordion = this.closest('.accordion');
if (!target) return;
// Check if we should close others (default behavior)
const closeOthers = !accordion.hasAttribute('data-allow-multiple');
if (closeOthers) {
// Close all other items in this accordion
accordion.querySelectorAll('.accordion-collapse.show').forEach(collapse => {
if (collapse !== target) {
collapse.classList.remove('show');
const btn = accordion.querySelector(`[data-accordion="${collapse.id}"]`);
if (btn) btn.classList.add('collapsed');
}
});
}
// Toggle current item
target.classList.toggle('show');
this.classList.toggle('collapsed');
});
});
});
data-allow-multiple attribute to the accordion container to allow multiple items to be open at once.
Utility Classes
Tani provides a comprehensive set of utility classes for common styling needs.
Spacing Utilities
Margin and padding utilities use a 5-level scale (0-5):
| Size | Value | Example |
|---|---|---|
| 0 | 0 | .m-0, .p-0 |
| 1 | 0.25rem | .m-1, .p-1 |
| 2 | 0.5rem | .m-2, .p-2 |
| 3 | 1rem | .m-3, .p-3 |
| 4 | 1.5rem | .m-4, .p-4 |
| 5 | 3rem | .m-5, .p-5 |
t (top),
r (right), b (bottom), l (left), x
(horizontal), y (vertical).
Display Utilities
| Class | Property |
|---|---|
.d-none |
display: none; |
.d-inline |
display: inline; |
.d-inline-block |
display: inline-block; |
.d-block |
display: block; |
.d-flex |
display: flex; |
Flexbox Utilities
| Class | Property |
|---|---|
.d-flex |
display: flex; |
.flex-row |
flex-direction: row; |
.flex-column |
flex-direction: column; |
.justify-content-start |
justify-content: flex-start; |
.justify-content-center |
justify-content: center; |
.justify-content-between |
justify-content: space-between; |
.align-items-start |
align-items: flex-start; |
.align-items-center |
align-items: center; |
Text Utilities
| Class | Property |
|---|---|
.text-left |
text-align: left; |
.text-center |
text-align: center; |
.text-right |
text-align: right; |
.text-muted |
color: #6c757d; |
.text-primary |
color: var(--primary); |
Border Radius Utilities
| Class | Property |
|---|---|
.rounded-0 |
border-radius: 0 !important; |
.rounded-1 |
border-radius: 5px !important; |
.rounded-2 |
border-radius: 10px !important; |
.rounded-3 |
border-radius: 15px !important; |
.rounded-4 |
border-radius: 20px !important; |
.rounded-5 |
border-radius: 25px !important; |
Font Size Utilities
| Class | Property |
|---|---|
.fs-1 |
font-size: 1.5rem !important; |
.fs-2 |
font-size: 1.25rem !important; |
.fs-3 |
font-size: 1.12rem !important; |
.fs-4 |
font-size: 0.87rem !important; |
.fs-5 |
font-size: 0.75rem !important; |
Font Weight Utilities
| Class | Property |
|---|---|
.font-bold |
font-weight: bold !important; |
.font-normal |
font-weight: normal !important; |
Shadow Utilities
| Class | Property |
|---|---|
.shadow-sm |
box-shadow: var(--shadow-sm) !important; |
.shadow |
box-shadow: var(--box-shadow) !important; |
.shadow-lg |
box-shadow: var(--shadow-lg) !important; |
.shadow-none |
box-shadow: none !important; |
Typography
Tani provides a consistent typography system with headings, paragraphs, and lists.
Headings
h1. Heading
h2. Heading
h3. Heading
h4. Heading
h5. Heading
h6. Heading
<h1>h1. Heading</h1>
<h2>h2. Heading</h2>
<h3>h3. Heading</h3>
<h4>h4. Heading</h4>
<h5>h5. Heading</h5>
<h6>h6. Heading</h6>
Paragraphs
This is a paragraph with default styling. It has appropriate line height and margins for readability.
This is a muted paragraph using the .text-muted class.
<p>This is a paragraph with default styling.</p>
<p class="text-muted">This is a muted paragraph...</p>