<section class="section docs-section" id="forms">
<h2 class="section-title">Forms</h2>
<p class="docs-section-description">
Формы используют `.form-group`, `.label` и `.input`.
Ошибки задаются на label через `.error`, а пояснение выводится соседним `.input-info`.
</p>
<div class="block form-group">
<label class="label">
Text input
<input type="text" class="input" placeholder="Введите текст">
</label>
</div>
<div class="block form-group">
<label class="label error">
Text input
<i class="ph ph-user"></i>
<input type="text" class="input" placeholder="Введите текст">
</label>
<div class="input-info"><i class="ph ph-warning-circle"></i> Field cannot be empty</div>
</div>
<div class="block form-group">
<label class="label">
Password
<input type="password" class="input" placeholder="••••••">
</label>
</div>
<div class="block form-grid">
<div class="form-group">
<label class="label success">
Validated
<input type="text" class="input" value="Release Plan">
</label>
<div class="input-info"><i class="ph ph-check-circle"></i> Looks good</div>
</div>
<div class="form-group">
<label class="label warning">
Needs review
<input type="text" class="input" value="Draft">
</label>
<div class="input-info"><i class="ph ph-warning-circle"></i> Check before publishing</div>
</div>
<div class="form-group">
<label class="label">
Readonly
<input type="text" class="input" value="run_1048" readonly>
</label>
</div>
<div class="form-group">
<label class="label">
Disabled
<input type="text" class="input" value="Locked" disabled>
</label>
</div>
</div>
<div class="block form-grid">
<div class="form-group">
<label class="label">
Search
<i class="ph ph-magnifying-glass"></i>
<input type="search" class="input" value="release" placeholder="Search">
</label>
</div>
<div class="form-group">
<label class="label">
Number
<input type="number" class="input" value="42" min="0" max="100">
</label>
</div>
</div>
<div class="block">
<fieldset class="fieldset">
<legend class="legend">Date & Time</legend>
<div class="form-grid">
<div class="form-group">
<label class="label">
Date
<i class="ph ph-calendar-blank"></i>
<input type="date" class="input input-date" value="2026-04-23" data-date-picker>
</label>
</div>
<div class="form-group">
<label class="label">
Time
<i class="ph ph-clock"></i>
<input type="time" class="input input-time" value="14:30" data-date-picker>
</label>
</div>
<div class="form-group">
<label class="label">
Date & time
<i class="ph ph-calendar-dots"></i>
<input type="datetime-local" class="input input-datetime" value="2026-04-23T14:30" data-date-picker>
</label>
</div>
<div class="form-group">
<label class="label">
Month
<i class="ph ph-calendar"></i>
<input type="month" class="input input-month" value="2026-04" data-date-picker>
</label>
</div>
</div>
</fieldset>
</div>
<div class="block form-group">
<label class="label">
Text Area
<i class="ph ph-text-aa"></i>
<textarea class="input" placeholder="Введите текст"></textarea>
</label>
</div>
<div class="block">
<fieldset class="fieldset">
<legend class="legend">Fieldset</legend>
<div class="form-grid">
<div class="form-group">
<label class="label">
First name
<input type="text" class="input" value="Ada">
</label>
</div>
<div class="form-group">
<label class="label">
Last name
<input type="text" class="input" value="Lovelace">
</label>
</div>
</div>
</fieldset>
</div>
<div class="block">
<label class="file-upload">
<i class="ph ph-upload-simple"></i>
Upload file
<input type="file">
</label>
</div>
<div class="block">
<div class="file-upload-panel">
<form class="file-upload-form" action="#" method="post" enctype="multipart/form-data">
<div class="file-upload-header">
<div class="file-upload-heading">
<h3 class="file-upload-title">Upload files</h3>
<p class="file-upload-description">Attach documents, archives, screenshots or product images for review.</p>
</div>
<span class="badge badge-info">Max 12 MB</span>
</div>
<label class="file-upload-dropzone">
<span class="file-upload-icon" aria-hidden="true">
<i class="ph ph-cloud-arrow-up"></i>
</span>
<span class="file-upload-body">
<span class="file-upload-primary">Choose files</span>
<span class="file-upload-secondary">Images get thumbnails, other files show their type</span>
</span>
<input type="file" name="files" multiple data-file-upload-input>
</label>
<div class="file-upload-preview" data-file-upload-preview hidden></div>
<div class="file-upload-actions">
<button class="btn btn-secondary btn-small" type="reset">Reset</button>
<button class="btn btn-accent btn-small with-icon" type="submit">
<i class="ph ph-upload-simple"></i>
Upload
</button>
</div>
</form>
</div>
</div>
<div class="block range">
<label class="label">
Range
<input type="range" min="0" max="100" value="64">
</label>
</div>
<div class="block form-group">
<label class="label">
Select
<div class="select-wrap">
<select class="input select">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</label>
</div>
<div class="block form-group">
<label class="label">
Select
<i class="ph ph-selection-all"></i>
<div class="select-wrap">
<select class="input select">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</label>
</div>
<div class="block form-group">
<label class="label error">
Advanced Select
<i class="ph ph-user"></i>
<input type="text" class="input advanced-select-input" placeholder="Search">
</label>
<div class="advanced-select-container"></div>
<div class="input-info"><i class="ph ph-warning-circle"></i> Field cannot be empty</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", e => {
setTimeout(() => {
document.querySelector(".advanced-select-container").append(advancedSelect(
document.querySelector(".advanced-select-input"),
{
"name_1": "Joe",
"name_2": "James",
"name_3": "Jack",
"name_4": "Eliza",
"name_5": "Emily",
"name_6": "Eleanor",
"name_7": "Ella",
"name_8": "Ellie",
"name_9": "John",
"name_10": "David"
},
"Nothing found"
));
}, 300);
});
</script>
<div class="block form-group">
<label class="label">Tags</label>
<div class="tag-input" id="demo-tag-input">
<div class="tag-input-wrap">
<span class="chip chip-secondary">frontend<button class="chip-remove" type="button" aria-label="Remove frontend"><i class="ph ph-x"></i></button></span>
<span class="chip chip-secondary">vue<button class="chip-remove" type="button" aria-label="Remove vue"><i class="ph ph-x"></i></button></span>
<input type="text" class="tag-input-field" placeholder="Add item…">
</div>
</div>
<div class="input-info"><i class="ph ph-info"></i> Press Enter to add, Backspace to remove last</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", e => {
const container = document.getElementById("demo-tag-input");
const wrap = container.querySelector(".tag-input-wrap");
const field = container.querySelector(".tag-input-field");
function addTag(text) {
const value = text.trim();
if(!value) return;
const existing = Array.from(wrap.querySelectorAll(".chip")).map(c => c.childNodes[0].textContent.trim());
if(existing.includes(value)) return;
const chip = document.createElement("span");
chip.className = "chip chip-secondary";
chip.innerHTML = `${value}<button class="chip-remove" type="button" aria-label="Remove ${value}"><i class="ph ph-x"></i></button>`;
wrap.insertBefore(chip, field);
}
function removeTag(btn) {
btn.closest(".chip")?.remove();
}
wrap.addEventListener("click", e => {
if(e.target.closest(".chip-remove")) {
removeTag(e.target.closest(".chip-remove"));
} else {
field.focus();
}
});
field.addEventListener("keydown", e => {
if(e.key === "Enter") {
e.preventDefault();
addTag(field.value);
field.value = "";
}
if(e.key === "Backspace" && !field.value) {
const chips = wrap.querySelectorAll(".chip");
if(chips.length) chips[chips.length - 1].remove();
}
});
});
</script>
<div class="block" id="demo-repeater">
<div class="repeater">
<div class="repeater-header">
<span class="repeater-title">Fields</span>
<button class="btn btn-secondary btn-small with-icon" type="button" id="demo-repeater-add">
<i class="ph ph-plus"></i>
Add field
</button>
</div>
<div class="repeater-list" id="demo-repeater-list">
<div class="repeater-item">
<div class="repeater-item-body form-grid">
<div class="form-group">
<label class="label">
Field name
<input type="text" class="input" value="username">
</label>
</div>
<div class="form-group">
<label class="label">
Value
<input type="text" class="input" value="">
</label>
</div>
</div>
<div class="repeater-item-actions">
<button class="btn-icon btn-icon-sm demo-repeater-remove" type="button" aria-label="Remove" disabled>
<i class="ph ph-trash"></i>
</button>
</div>
</div>
<div class="repeater-item">
<div class="repeater-item-body form-grid">
<div class="form-group">
<label class="label">
Field name
<input type="text" class="input" value="password">
</label>
</div>
<div class="form-group">
<label class="label">
Value
<input type="text" class="input" value="">
</label>
</div>
</div>
<div class="repeater-item-actions">
<button class="btn-icon btn-icon-sm demo-repeater-remove" type="button" aria-label="Remove">
<i class="ph ph-trash"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", e => {
const list = document.getElementById("demo-repeater-list");
const addBtn = document.getElementById("demo-repeater-add");
function updateRemoveButtons() {
const items = list.querySelectorAll(".repeater-item");
items.forEach((item, index) => {
const btn = item.querySelector(".demo-repeater-remove");
if(btn) btn.disabled = items.length <= 1;
});
}
function createItem(name = "") {
const div = document.createElement("div");
div.className = "repeater-item";
div.innerHTML = `
<div class="repeater-item-body form-grid">
<div class="form-group">
<label class="label">
Field name
<input type="text" class="input" value="${name}">
</label>
</div>
<div class="form-group">
<label class="label">
Value
<input type="text" class="input" value="">
</label>
</div>
</div>
<div class="repeater-item-actions">
<button class="btn-icon btn-icon-sm demo-repeater-remove" type="button" aria-label="Remove">
<i class="ph ph-trash"></i>
</button>
</div>
`;
div.querySelector(".demo-repeater-remove").addEventListener("click", () => {
div.remove();
updateRemoveButtons();
});
return div;
}
list.querySelectorAll(".demo-repeater-remove").forEach(btn => {
btn.addEventListener("click", e => {
e.target.closest(".repeater-item").remove();
updateRemoveButtons();
});
});
addBtn.addEventListener("click", () => {
list.append(createItem());
updateRemoveButtons();
});
});
</script>
<div class="block form-group">
<label class="checkbox">
<input type="checkbox">
<span class="checkbox-control" aria-hidden="true"></span>
<span class="checkbox-label">Checkbox 1</span>
</label>
<label class="checkbox mt-3">
<input type="checkbox">
<span class="checkbox-control" aria-hidden="true"></span>
<span class="checkbox-label">Checkbox 2</span>
</label>
<label class="checkbox mt-3">
<input type="checkbox" disabled>
<span class="checkbox-control" aria-hidden="true"></span>
<span class="checkbox-label">Disabled</span>
</label>
<label class="checkbox mt-3">
<input type="checkbox" disabled checked>
<span class="checkbox-control" aria-hidden="true"></span>
<span class="checkbox-label">Disabled & Checked</span>
</label>
</div>
<div class="block form-group">
<label class="radio">
<input type="radio" name="radio-demo">
<span class="radio-control" aria-hidden="true"></span>
<span class="radio-label">Radio 1</span>
</label>
<label class="radio ml-2">
<input type="radio" name="radio-demo">
<span class="radio-control" aria-hidden="true"></span>
<span class="radio-label">Radio 2</span>
</label>
<label class="radio ml-2">
<input type="radio" name="radio-demo" disabled>
<span class="radio-control" aria-hidden="true"></span>
<span class="radio-label">Radio 3</span>
</label>
</div>
<div class="code-example">
<div class="code-example-header">
<span class="code-example-title">Forms HTML</span>
<button class="btn-icon btn-icon-sm" type="button" aria-label="Copy"><i class="ph ph-copy"></i></button>
</div>
<pre><code class="language-html"><div class="form-group">
<label class="label error">
Project name
<i class="ph ph-user"></i>
<input class="input" type="text" placeholder="Launch Plan">
</label>
<div class="input-info">
<i class="ph ph-warning-circle"></i>
Field cannot be empty
</div>
</div>
<fieldset class="fieldset">
<legend class="legend">Fieldset</legend>
<label class="label success">
Validated
<input class="input" type="text" value="Release Plan">
</label>
</fieldset>
<label class="file-upload">
Upload file
<input type="file">
</label>
<fieldset class="fieldset">
<legend class="legend">Date & Time</legend>
<div class="form-grid">
<label class="label">
Date
<i class="ph ph-calendar-blank"></i>
<input class="input input-date" type="date" value="2026-04-23" data-date-picker>
</label>
<label class="label">
Time
<i class="ph ph-clock"></i>
<input class="input input-time" type="time" value="14:30" data-date-picker>
</label>
<label class="label">
Date & time
<i class="ph ph-calendar-dots"></i>
<input class="input input-datetime" type="datetime-local" value="2026-04-23T14:30" data-date-picker>
</label>
</div>
</fieldset>
<div class="file-upload-panel">
<form class="file-upload-form" enctype="multipart/form-data">
<div class="file-upload-header">
<div class="file-upload-heading">
<h3 class="file-upload-title">Upload files</h3>
<p class="file-upload-description">Attach documents, archives or images.</p>
</div>
<span class="badge badge-info">Max 12 MB</span>
</div>
<label class="file-upload-dropzone">
<span class="file-upload-icon" aria-hidden="true">
<i class="ph ph-cloud-arrow-up"></i>
</span>
<span class="file-upload-body">
<span class="file-upload-primary">Choose files</span>
<span class="file-upload-secondary">Images get thumbnails, other files show their type</span>
</span>
<input type="file" name="files" multiple data-file-upload-input>
</label>
<div class="file-upload-preview" data-file-upload-preview hidden></div>
<div class="file-upload-actions">
<button class="btn btn-secondary btn-small" type="reset">Reset</button>
<button class="btn btn-accent btn-small" type="submit">Upload</button>
</div>
</form>
</div></code></pre>
</div>
<div class="code-example">
<div class="code-example-header">
<span class="code-example-title">Tag Input HTML</span>
<button class="btn-icon btn-icon-sm" type="button" aria-label="Copy"><i class="ph ph-copy"></i></button>
</div>
<pre><code class="language-html"><div class="tag-input">
<div class="tag-input-wrap">
<span class="chip chip-secondary">frontend
<button class="chip-remove" type="button" aria-label="Remove frontend">
<i class="ph ph-x"></i>
</button>
</span>
<input type="text" class="tag-input-field" placeholder="Add item…">
</div>
</div>
<div class="input-info"><i class="ph ph-info"></i> Press Enter to add</div></code></pre>
</div>
<div class="code-example">
<div class="code-example-header">
<span class="code-example-title">Vue: GnTagInput</span>
<button class="btn-icon btn-icon-sm" type="button" aria-label="Copy"><i class="ph ph-copy"></i></button>
</div>
<pre><code class="language-html"><GnTagInput
v-model="tags"
label="Tags"
placeholder="Add item…"
help="Press Enter to add, Backspace to remove last"
separator=","
:unique="true"
:max-items="10"
/></code></pre>
</div>
<div class="code-example">
<div class="code-example-header">
<span class="code-example-title">Repeater HTML</span>
<button class="btn-icon btn-icon-sm" type="button" aria-label="Copy"><i class="ph ph-copy"></i></button>
</div>
<pre><code class="language-html"><div class="repeater">
<div class="repeater-header">
<span class="repeater-title">Fields</span>
<button class="btn btn-secondary btn-small" type="button">Add field</button>
</div>
<div class="repeater-list">
<div class="repeater-item">
<div class="repeater-item-body form-grid">...</div>
<div class="repeater-item-actions">
<button class="btn-icon btn-icon-sm" type="button">...</button>
</div>
</div>
</div>
</div></code></pre>
</div>
<div class="code-example">
<div class="code-example-header">
<span class="code-example-title">Vue: GnRepeater</span>
<button class="btn-icon btn-icon-sm" type="button" aria-label="Copy"><i class="ph ph-copy"></i></button>
</div>
<pre><code class="language-html"><GnRepeater v-model="fields" label="Fields" add-label="Add field">
<template #item="{ item, index, remove }">
<div class="form-grid">
<GnInput v-model="item.name" label="Name" />
<GnInput v-model="item.value" label="Value" />
</div>
</template>
</GnRepeater></code></pre>
</div>
</section>