| Unit | Net | VAT | Status | Actions | ||
|---|---|---|---|---|---|---|
| ART-001 | Web Design Package S | flat | €1,200.00 | 20% | active | |
| ART-002 | Hosting Standard | pcs | €9.90 | 20% | active | |
| ART-003 | SEO Consulting | h | €95.00 | 20% | inactive | |
| ART-004 | Logo Design | flat | €450.00 | 20% | draft | |
| ART-005 | Newsletter Setup | flat | €350.00 | 20% | active |
$table = new Table('articles');
$table->setData($articles)
->search(['article_number', 'name'])
->column('article_number', 'Article No.', ['width' => '120px', 'sortable' => true, 'nowrap' => true, 'muted' => true])
->column('name', 'Description', ['sortable' => true, 'nowrap' => true])
->column('unit', 'Unit', ['width' => '80px', 'nowrap' => true])
->column('net_price', 'Net', ['format' => 'currency', 'align' => 'right', 'width' => '100px', 'nowrap' => true])
->column('tax_rate', 'VAT', ['format' => 'percent', 'width' => '80px', 'nowrap' => true])
->column('is_active', 'Status', ['format' => 'label', 'nowrap' => true])
->filter('is_active', 'select', ['options' => ['active' => 'Active', 'inactive' => 'Inactive', 'draft' => 'Draft'], 'placeholder' => 'All Status'])
->button('edit', ['icon' => 'edit', 'class' => 'primary', 'position' => 'right', 'params' => ['id' => 'article_id']])
->button('delete', ['icon' => 'delete', 'class' => 'danger', 'position' => 'right', 'params' => ['id' => 'article_id']])
->newButton('New Article', ['icon' => 'add'])
->nowrap(true)
->paginate(5)
->render();| Due | Status | Actions | Actions | ||||
|---|---|---|---|---|---|---|---|
| RE-2026-001 | Mustermann GmbH | Feb 1, 2026 | Mar 1, 2026 | €1,450.00 | paid | ||
| RE-2026-002 | Tech Solutions AG | Feb 5, 2026 | Mar 5, 2026 | €3,200.00 | pending | ||
| RE-2026-003 | Weber & Partner | Feb 8, 2026 | Mar 8, 2026 | €890.50 | overdue | ||
| RE-2026-004 | Digital Agentur Wien | Feb 10, 2026 | Mar 10, 2026 | €5,600.00 | draft | ||
| RE-2026-005 | Startup Hub Vienna | Feb 12, 2026 | Mar 12, 2026 | €2,100.00 | paid |
| Role | Active | ||
|---|---|---|---|
| Martin Huber | [email protected] | admin | ✓ |
| Anna Schneider | [email protected] | editor | ✓ |
| Thomas Berger | [email protected] | viewer | – |
| Lisa Wagner | [email protected] | editor | ✓ |
| Peter Gruber | [email protected] | admin | – |
| Name | Value | Status |
|---|---|---|
| Widget A | 1.200 € | active |
| Widget B | 340 € | inactive |
| Widget C | 890 € | active |
| Name | Value | Status |
|---|---|---|
| Widget A | 1.200 € | active |
| Widget B | 340 € | inactive |
| Widget C | 890 € | active |
| Name | Value | Status |
|---|---|---|
| Widget A | 1.200 € | active |
| Widget B | 340 € | inactive |
| Widget C | 890 € | active |
$table->size('sm'); // compact
$table->size('md'); // standard (default)
$table->size('lg'); // spacious| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
| Description | Price | Status |
|---|---|---|
| Web Design Package | 1.200 € | active |
| Hosting Standard | 9,90 € | active |
| SEO Consulting | 95 € | inactive |
| Logo Design | 450 € | draft |
First column as label/key — ideal for detail views.
| Company name | SSI Schaefer IT Solutions GmbH |
| Founded | 2003 |
| Location | Wien, Oesterreich |
| Employees | 24 |
| Website | ssi.at |
| Status | Active |
$table->variant('default'); // Default
$table->variant('bordered'); // Full border lines
$table->variant('striped'); // Zebra stripes
$table->variant('celled'); // Grid lines around each cell
$table->variant('padded'); // Extra spacing
$table->variant('compact'); // Compact, more rows
$table->variant('selectable'); // Hover cursor, clickable
$table->variant('minimal'); // Only separator, no border
$table->variant('flat'); // Completely flat
$table->variant('inverted'); // Dark table (also in Light Mode)
// A variant is one slot: a second call REPLACES the first, it does not add.
$table->variant('celled')->variant('padded'); // celled is dropped — padded wins
// Variant and size are separate slots, so those two do stack:
$table->variant('striped')->size('sm');Resize the browser window to <768px to see the mobile layout.
| Article No. | Description | Price | Status |
|---|---|---|---|
| ART-001 | Webdesign Paket S | 1.200 € | active |
| ART-002 | Hosting Standard | 9,90 € | active |
| ART-003 | SEO Beratung | 95 € | inactive |
| Article No. | Description | Price | Status |
|---|---|---|---|
| ART-001 | Webdesign Paket S | 1.200 € | active |
| ART-002 | Hosting Standard | 9,90 € | active |
| ART-003 | SEO Beratung | 95 € | inactive |
| Article No. | Description | Price | Status |
|---|---|---|---|
| ART-001 | Webdesign Paket S | 1.200 € | active |
| ART-002 | Hosting Standard | 9,90 € | active |
| ART-003 | SEO Beratung | 95 € | inactive |
// Demo 1: Complete article table with pagination
$table = new Table('articles');
$table->setData($articles)
->search(['article_number', 'name'])
->column('name', 'Description', ['sortable' => true])
->column('net_price', 'Net', ['format' => 'currency'])
->column('tax_rate', 'VAT', ['format' => 'percent'])
->column('is_active', 'Status', ['format' => 'label'])
->button('edit', ['icon' => 'edit', 'class' => 'primary'])
->button('delete', ['icon' => 'delete', 'class' => 'danger'])
->newButton('New Article', ['icon' => 'add'])
->nowrap(true)
->paginate(5)
->render();
// Mobile-Responsive
$table->mobile('card'); // Cards on mobile (default)
$table->mobile('scroll'); // Horizontal Scroll
$table->column('desc', 'Description', ['hideOnMobile' => true]);$form = new Form('article_form');
$form->action('save/process_article.php')
->ajax()
->hidden('article_id', '')
->row()
->field('article_number', 'Article No.', 'text', ['required' => true, 'width' => 8])
->field('name', 'Description', 'text', ['required' => true, 'width' => 8])
->endRow()
->field('description', 'Description', 'textarea', ['rows' => 3])
->row()
->field('unit', 'Unit', 'select', ['options' => ['Stk' => 'Piece', 'h' => 'Hour', 'psch' => 'Flat rate'], 'width' => 5])
->field('net_price', 'Net price', 'number', ['step' => '0.01', 'width' => 5])
->field('tax_rate', 'VAT %', 'select', ['options' => ['20' => '20%', '10' => '10%', '0' => '0%'], 'width' => 6])
->endRow()
->field('is_active', 'Active', 'toggle', ['inline' => true])
->submit('Save')
->render();Local vendor bundle (assets/ckeditor5/), no CDN. Initialization via IntersectionObserver — works in tabs and modals too.
Trash icon only appears when a value is set — disappears automatically after clearing.
Uniform height of 44px for all input types. No browser spinner for number.
Searchable Select – Country selection with search field
Multi-Select – Select multiple categories (with chips)
Ajax Select – Customer search via API (min. 2 characters)
// Grid Layout (16 columns)
$form->row()
->field('name', 'Name', 'text', ['width' => 8])
->field('email', 'E-Mail', 'email', ['width' => 8])
->endRow()
->field('agree', 'Accept terms', 'checkbox', ['checked' => true])
->field('payment', 'Payment method', 'radio', [
'options' => ['card' => 'Credit card', 'bank' => 'Bank transfer'],
'value' => 'card', 'inline' => true
])
->field('active', 'Active', 'toggle', ['inline' => true])
->field('volume', 'Volume', 'range', ['min' => 0, 'max' => 100, 'value' => 50])
->field('doc', 'Document', 'file', ['accept' => '.pdf', 'multiple' => true, 'maxSize' => '10MB'])
->field('content', 'Content', 'richtext', ['preset' => 'full'])
->field('datum', 'Date', 'date', ['value' => date('Y-m-d'), 'clearable' => true])
->field('farbe', 'Color', 'color', ['value' => '#6750a4'])
->field('preis', 'Price', 'number', ['placeholder' => '0.00'])Select files via drag & drop or click
Type, size (min/max), count and total size
Image thumbnails directly in the upload zone
Progress indicator, states, errors per file
Document upload with type filter and size limit.
Multi-upload with image thumbnails directly in the zone.
All options combined: types, min/max size, total limit, file count.
Manually cycle queue items through states: pending → uploading → done / error.
Zone with strict limits — PDF only, max. 100 KB.
Styled native color input — color swatch on the left (clickable) + hex field on the right. Swatch and hex value sync automatically, validated as #RRGGBB.
#RRGGBB values are accepted
Wrap any form in .gk-form-compact for reduced spacing. All elements — inputs, selects, toggles, checkboxes — scale down proportionally.
.gk-form-compact<!-- Normal (default) -->
<form>
<div class="gk-field">...</div>
</form>
<!-- Compact -->
<form class="gk-form-compact">
<div class="gk-field">...</div>
</form>
<!-- Also works as wrapper -->
<div class="gk-form-compact">
<div class="gk-card">
<div class="gk-field">...</div>
</div>
</div>
Card grid for structured content — responsive, with header, body and footer.
.gk-cards // Auto-Grid (min 280px) .gk-cards-2 / -3 / -4 // Fixed columns .gk-card .gk-card-header / .gk-card-body / .gk-card-footer .gk-card-link // Clickable Card
$stats = new StatCards('dashboard');
$stats->card('Customers', 248, ['format' => 'number', 'color' => 'blue'])
->card('Revenue', 84250.00, ['format' => 'currency', 'color' => 'green'])
->card('Outstanding', 12480.00, ['format' => 'currency', 'color' => 'red'])
->render();Container for related content — single, stacked or with header.
A simple segment visually groups related content.
Segments can have a header to describe the content.
Create account and confirm email.
Fill out profile and configure settings.
Create first project and get started.
With shadow — stands out more from the background.
Muted background — for secondary content.
Less padding — for denser layouts.
.gk-segment / .gk-segment-raised / .gk-segment-muted / .gk-segment-compact .gk-segment-padded / .gk-segment-basic .gk-segments > .gk-segment // Stacked
Notices, warnings and status messages for users.
.gk-message / .gk-message-info / .gk-message-success .gk-message-warning / .gk-message-error / .gk-message-compact .gk-message-dismiss // Close button
Collapsible content areas — individually or as a group.
<div class="gk-accordion" data-gk-single>
<div class="gk-accordion-item open">
<button class="gk-accordion-trigger">
<span>Question?</span>
<span class="material-icons">expand_more</span>
</button>
<div class="gk-accordion-content">
<div class="gk-accordion-body">Answer...</div>
</div>
</div>
</div>
// data-gk-single: only one item open at a time
// .open: item open by defaultProfile pictures with initials fallback, status dot and groups.
.gk-avatar.gk-avatar-lg // Sizes: xs, sm, md, lg, xl .gk-avatar-status.online // Status: online, away, busy, offline .gk-avatar-group // Stacked group .gk-avatar-square // Square instead of round
Image grid with lazy loading, hover overlay and lightbox (arrow keys, Escape).
<div class="gk-gallery">
<div class="gk-gallery-item" data-lightbox="full.jpg" data-caption="Caption" data-lazy>
<img data-src="thumb.jpg" alt="">
<div class="gk-gallery-overlay">
<span class="gk-gallery-caption">Caption</span>
</div>
</div>
</div>
// Variants:
.gk-gallery-sm // Smaller thumbnails (100px min)
.gk-gallery-lg // Larger thumbnails (220px min)
.gk-gallery-masonry // Pinterest layout (columns)
// Lightbox: arrow keys, Escape, click outside to close
// data-lazy: load images only when visiblePath navigation for orientation in nested areas.
Page content here...
<nav class="gk-breadcrumb">
<a href="#"><span class="material-icons">home</span></a>
<span class="gk-breadcrumb-sep"><span class="material-icons">chevron_right</span></span>
<a href="#">Products</a>
<span class="gk-breadcrumb-sep">...</span>
<span class="gk-breadcrumb-current">Current Page</span>
</nav>Tab navigation for switching between content areas.
This is the overview — the first tab is active by default.
Detail information is displayed here.
Settings and configuration options.
<div class="gk-tabs">
<div class="gk-tab-nav">
<button class="gk-tab-btn gk-active" data-tab="tab-1">Tab 1</button>
<button class="gk-tab-btn" data-tab="tab-2">Tab 2</button>
</div>
<div class="gk-tab-panel gk-active" data-tab="tab-1">Content 1</div>
<div class="gk-tab-panel" data-tab="tab-2">Content 2</div>
</div>Combination examples showing the full potential of GridKit.
Tabs for main categories, accordion for details — a common pattern in admin UIs.
Dashboard view with status message and data table in a segment.
| Server | Status | CPU | RAM | Uptime |
|---|---|---|---|---|
| Baerli (server8) | Online | 12% | 4.2 GB | 47 days |
| Theo (server7) | Online | 8% | 3.8 GB | 23 days |
| Waldi (server6) | Online | 15% | 5.1 GB | 31 days |
Page navigation — automatically generated by Table, but can also be used standalone.
<nav class="gk-pagination">
<button class="gk-page-btn" disabled>«</button>
<button class="gk-page-btn gk-active">1</button>
<button class="gk-page-btn">2</button>
<button class="gk-page-btn">»</button>
</nav>$chips = new FilterChips('status-filter', 'status');
$chips->chip('', 'All', ['count' => 152])
->chip('active', 'Active', ['count' => 89, 'color' => 'green'])
->render();$years = new YearFilter('year-nav', 'year');
$years->range(2022, 2026)->render();One unified filter/search bar for all tables. Three fixed sections: Status row · Toolbar · Advanced (collapsible). Stops every page from looking different.
TableHeader::make('exp')
->status(fn() => $statusChips->render())
->search('q', $q, 'Search…', ['live' => 'exp-live'])
->filter(fn() => $yearFilter->render())
->filter('<select class="gk-filter">…</select>')
->advanced(fn() => renderDateRange())
->reset('/faktura/expenses')
->render();Built-in formatting for table columns: currency, percent, date, boolean, label, email.
| Currency | Percent | Date | Boolean | Label | |
|---|---|---|---|---|---|
| €1,234.56 | 20% | Feb 13, 2026 | ✓ | paid | [email protected] |
| €99.00 | 10% | Jan 28, 2026 | – | pending | [email protected] |
| €5,500.00 | 0% | Dec 1, 2025 | ✓ | cancelled |
->column('amount', 'Amount', ['format' => 'currency']) // €1,234.56 (follows the locale)
->column('tax', 'VAT', ['format' => 'percent']) // 20%
->column('date', 'Date', ['format' => 'date']) // Feb 13, 2026 (follows the locale)
->column('active', 'Active', ['format' => 'boolean']) // ✓ / –
->column('status', 'Status', ['format' => 'label']) // Colored label
->column('email', 'E-Mail', ['format' => 'email']) // mailto: LinkSystem-wide quick search. GridKit provides the widget only —
WHAT is searched is decided by your backend endpoint. Opens with
Ctrl+K (Cmd+K on Mac) or any element
carrying data-gk-search. Arrow keys select, Enter opens, Escape closes.
GK.search.init({
url: '/demo/api/quicksearch.php',
hotkey: 'ctrl+k',
minLength: 0
});
// Server response. The German key names — gruppen / titel / treffer /
// untertitel / betrag — are still read as a fallback, but this is the shape:
{ "groups": [
{ "title": "Transactions",
"items": [
{ "title": "Hetzner",
"subtitle": "Jul 7, 2026",
"amount": "€64.80",
"url": "/transactions/2",
"icon": "account_balance" } ] } ] }Toast notifications for success, error and info messages. Disappear after 3 seconds.
GK.toast.success('Successfully saved!');
GK.toast.error('Error while saving!');
GK.toast.warning('Warning: Limit reached!');
GK.toast.info('3 new entries');Confirm dialogs as clean modals. Promise-based, with danger mode for destructive actions.
GK.confirm('Send invoice?', {
title: 'Send Invoice', confirmText: 'Send'
}).then(ok => { if (ok) GK.toast.success('Sent!'); });
GK.confirm('Really delete?', {
title: 'Delete', confirmText: 'Delete', danger: true
}).then(ok => { if (ok) { /* ... */ } });Modals in four sizes. Loaded via AJAX, backdrop click and ESC close them.
A modal loads a form via AJAX – the most common use case.
A large modal shows a customer list. Clicking "Edit" opens a second modal with the form.
GK.modal.open('Title', 'form/edit.php', {id: 42}, 'small');
GK.modal.open('Title', 'form/edit.php', {id: 42}, 'medium');
GK.modal.open('Title', 'form/edit.php', {id: 42}, 'large');
GK.modal.open('Title', 'form/edit.php', {id: 42}, 'full');
$table->button('edit', ['icon' => 'edit', 'modal' => 'edit_form'])
->modal('edit_form', 'Edit', 'form/edit.php', ['size' => 'large']);use GridKit\Button;
echo Button::render('Save', ['variant' => 'filled', 'color' => 'primary', 'icon' => 'save']);
echo Button::icon('edit', ['variant' => 'filled', 'color' => 'primary']);
echo Button::fab('add');
echo Button::fab('edit', ['extended' => true, 'label' => 'Edit']);Colored labels for status display — also usable standalone, not only in tables.
<span class="gk-label gk-label-green">Active</span> <span class="gk-label gk-label-orange">Draft</span> <span class="gk-label gk-label-red">Error</span> <span class="gk-label gk-label-gray">Archived</span> <span class="gk-label gk-label-blue">Info</span>
Tooltips appear automatically on buttons with a title attribute. Hover over the buttons:
// Tooltip via title attribute (CSS-only, no JS)
Button::icon('edit', ['title' => 'Edit']);
<button class="gk-btn" title="Tooltip-Text">...</button>Placeholder for empty tables or lists.
| Name | Status | |
|---|---|---|
inboxNo entries found | ||
<div class="gk-empty">No entries found</div>
Minimal (title + user with initials only)
$header = new Header();
echo $header->title('Invoices')
->breadcrumb(['Dashboard' => '/', 'Invoices'])
->search('Search...', 'q')
->action(Button::render('New', ['icon' => 'add', 'size' => 'sm']))
->user('Martin Huber', ['avatar' => '...', 'menu' => [...]])
->sticky()->render();Responsive sidebar navigation with groups, icons, badges and mobile toggle. Visible live on this page.
$sidebar = new Sidebar('main');
$sidebar->brand('My Project', 'dashboard', 'v0.7.0')
->group('Modules')
->item('Dashboard', '/dashboard', 'analytics', ['active' => true])
->item('Invoices', '/invoices', 'receipt_long', ['badge' => 3])
->render();
GK.sidebar.toggle(); GK.sidebar.open(); GK.sidebar.close();M3-compliant theme system with 6 themes and Dark/Light Mode.
Surface-container background with on-surface text colors.
Theme::set('ocean', 'dark');
echo Theme::bodyTag('gk-root');
echo Theme::switcher();
GK.theme.set('forest'); GK.theme.toggleMode(); GK.theme.restore();
Everything below this box is a gallery — it shows what exists. If you would
rather see what it is for, examples/invoices/ is a
working invoice manager: create, edit, delete, with search, filter, sort and
paging answered by the server. About 300 lines, no database.
| Amount | Date | Status | ||
|---|---|---|---|---|
| RE-2026-001 | Mustermann GmbH | €2,400.00 | Feb 1, 2026 | paid |
| RE-2026-002 | Technik AG | €5,800.00 | Feb 5, 2026 | pending |
| RE-2026-003 | Design Studio | €1,200.00 | Jan 15, 2026 | overdue |
| RE-2026-004 | Web Solutions | €3,600.00 | Feb 10, 2026 | paid |
| RE-2026-005 | Media House | €950.00 | Feb 12, 2026 | pending |
skeleton.php is the ready-made scaffolding for a new GridKit project. Just copy, adjust title + navigation, fill in sections — done.
cp /path/to/gridkit/skeleton.php my-project/index.php
Every page follows this structure — from skeleton.php to production.
Theme::set('indigo') · Layout::bodyTag('gk-root')
->brand() · ->group() · ->item(label, url, icon)
->title() · ->fixed() · ->user(name, opts)
<main class="gk-main"> · Tables, Forms, Cards…
<script src="gridkit.js">
Auth protects pages with session-based login. Passwords are stored as bcrypt hashes.
Pure CSS tooltips via data-gk-tooltip attribute. No JavaScript needed.
Rich tooltips use data-gk-tooltip-rich pointing to a hidden element. Supports links, formatting, and stays open on hover.
Rich tooltips support full HTML content including links, images, and interactive elements.
Learn more →Any element with a title attribute gets a GridKit popup — no markup needed. Opt out with data-gk-tip-off.
<!-- Simple tooltip (CSS-only) --> <span data-gk-tooltip="Hello!">Hover me</span> <!-- Position: top (default), bottom, left, right --> <span data-gk-tooltip="Bottom" data-gk-tooltip-pos="bottom">...</span> <!-- Multiline --> <span data-gk-tooltip="Long text here" data-gk-tooltip-wrap>...</span> <!-- Rich tooltip (HTML content) --> <span data-gk-tooltip-rich="#myTip">Hover</span> <div id="myTip">HTML content here</div>