|$ curl https://forge-ai.dev/api/markdown?path=docs/components/inputs
$cat docs/inputs-&-controls.md
updated Recently·12 min read·published
Inputs & Controls
◆CSS◆HTML◆UI◆Intermediate
12+ input controls — toggles, radio buttons, star rating, slider, file upload, color swatches, and input groups.
Toggle Switch
Sized toggle switches with labeled variants.
toggle
Live
untitled.html
HTML
| 1 | <div class="wrap"> |
| 2 | <label class="t"> |
| 3 | <input type="checkbox" checked /> |
| 4 | <span class="slider"> |
| 5 | </span> |
| 6 | Notifications |
| 7 | </label> |
| 8 | <label class="t"> |
| 9 | <input type="checkbox" /> |
| 10 | <span class="slider"> |
| 11 | </span> |
| 12 | Dark Mode |
| 13 | </label> |
| 14 | <label class="t sm"> |
| 15 | <input type="checkbox" checked /> |
| 16 | <span class="slider"> |
| 17 | </span> |
| 18 | Small |
| 19 | </label> |
| 20 | </div> |
untitled.css
CSS
| 1 | * { |
| 2 | margin:0; |
| 3 | padding:0; |
| 4 | box-sizing:border-box |
| 5 | } |
| 6 | body { |
| 7 | font-family:system-ui, |
| 8 | sans-serif; |
| 9 | background:#0D0D0D; |
| 10 | padding:24px |
| 11 | } |
| 12 | .wrap { |
| 13 | display:flex; |
| 14 | flex-direction:column; |
| 15 | gap:16px; |
| 16 | max-width:280px; |
| 17 | margin:20px auto |
| 18 | } |
| 19 | .t { |
| 20 | display:flex; |
| 21 | align-items:center; |
| 22 | gap:12px; |
| 23 | font-size:13px; |
| 24 | color:#A0A0A0; |
| 25 | cursor:pointer |
| 26 | } |
| 27 | .t input { |
| 28 | display:none |
| 29 | } |
| 30 | .slider { |
| 31 | width:40px; |
| 32 | height:22px; |
| 33 | background:#222; |
| 34 | border-radius:11px; |
| 35 | position:relative; |
| 36 | transition:background .3s; |
| 37 | flex-shrink:0 |
| 38 | } |
| 39 | .slider::after { |
| 40 | content:''; |
| 41 | position:absolute; |
| 42 | top:3px; |
| 43 | left:3px; |
| 44 | width:16px; |
| 45 | height:16px; |
| 46 | border-radius:50%; |
| 47 | background:#808080; |
| 48 | transition:all .3s |
| 49 | } |
| 50 | .t input:checked+.slider { |
| 51 | background:rgba(0, |
| 52 | 255, |
| 53 | 65, |
| 54 | .3) |
| 55 | } |
| 56 | .t input:checked+.slider::after { |
| 57 | background:#00FF41; |
| 58 | transform:translateX(18px) |
| 59 | } |
| 60 | .sm .slider { |
| 61 | width:32px; |
| 62 | height:18px |
| 63 | } |
| 64 | .sm .slider::after { |
| 65 | width:12px; |
| 66 | height:12px; |
| 67 | top:3px; |
| 68 | left:3px |
| 69 | } |
| 70 | .sm input:checked+.slider::after { |
| 71 | transform:translateX(14px) |
| 72 | } |
Radio Button Group
Styled radio buttons as a segmented card selection.
radio
Live
untitled.html
HTML
| 1 | <div class="wrap"> |
| 2 | <label class="r"> |
| 3 | <input type="radio" name="plan" checked /> |
| 4 | <span class="radio-mark"> |
| 5 | </span> |
| 6 | <div> |
| 7 | <strong> |
| 8 | Free |
| 9 | </strong> |
| 10 | <p class="r-sub"> |
| 11 | $0/mo — basic features |
| 12 | </p> |
| 13 | </div> |
| 14 | </label> |
| 15 | <label class="r"> |
| 16 | <input type="radio" name="plan" /> |
| 17 | <span class="radio-mark"> |
| 18 | </span> |
| 19 | <div> |
| 20 | <strong> |
| 21 | Pro |
| 22 | </strong> |
| 23 | <p class="r-sub"> |
| 24 | $12/mo — advanced features |
| 25 | </p> |
| 26 | </div> |
| 27 | </label> |
| 28 | <label class="r"> |
| 29 | <input type="radio" name="plan" /> |
| 30 | <span class="radio-mark"> |
| 31 | </span> |
| 32 | <div> |
| 33 | <strong> |
| 34 | Enterprise |
| 35 | </strong> |
| 36 | <p class="r-sub"> |
| 37 | Custom pricing |
| 38 | </p> |
| 39 | </div> |
| 40 | </label> |
| 41 | </div> |
untitled.css
CSS
| 1 | * { |
| 2 | margin:0; |
| 3 | padding:0; |
| 4 | box-sizing:border-box |
| 5 | } |
| 6 | body { |
| 7 | font-family:system-ui, |
| 8 | sans-serif; |
| 9 | background:#0D0D0D; |
| 10 | padding:24px |
| 11 | } |
| 12 | .wrap { |
| 13 | max-width:340px; |
| 14 | margin:16px auto; |
| 15 | display:flex; |
| 16 | flex-direction:column; |
| 17 | gap:8px |
| 18 | } |
| 19 | .r { |
| 20 | display:flex; |
| 21 | align-items:center; |
| 22 | gap:12px; |
| 23 | padding:12px; |
| 24 | border-radius:8px; |
| 25 | border:1.5px solid #222; |
| 26 | cursor:pointer; |
| 27 | transition:all .2s |
| 28 | } |
| 29 | .r:hover { |
| 30 | border-color:#333 |
| 31 | } |
| 32 | .r input { |
| 33 | display:none |
| 34 | } |
| 35 | .r:has(input:checked) { |
| 36 | border-color:#00FF41; |
| 37 | background:rgba(0, |
| 38 | 255, |
| 39 | 65, |
| 40 | .04) |
| 41 | } |
| 42 | .radio-mark { |
| 43 | width:18px; |
| 44 | height:18px; |
| 45 | border-radius:50%; |
| 46 | border:2px solid #444; |
| 47 | display:flex; |
| 48 | align-items:center; |
| 49 | justify-content:center; |
| 50 | flex-shrink:0; |
| 51 | transition:all .2s |
| 52 | } |
| 53 | .radio-mark::after { |
| 54 | content:''; |
| 55 | width:8px; |
| 56 | height:8px; |
| 57 | border-radius:50%; |
| 58 | background:transparent; |
| 59 | transition:all .2s |
| 60 | } |
| 61 | .r input:checked+.radio-mark { |
| 62 | border-color:#00FF41 |
| 63 | } |
| 64 | .r input:checked+.radio-mark::after { |
| 65 | background:#00FF41 |
| 66 | } |
| 67 | .r strong { |
| 68 | font-size:13px; |
| 69 | color:#E0E0E0 |
| 70 | } |
| 71 | .r-sub { |
| 72 | font-size:11px; |
| 73 | color:#808080; |
| 74 | margin-top:2px |
| 75 | } |
Star Rating
Interactive star rating with hover and selected states.
rating
Live
untitled.html
HTML
| 1 | <div class="wrap"> |
| 2 | <div class="stars" style="--val:3"> |
| 3 | <span class="star" data-val="1"> |
| 4 | ★ |
| 5 | </span> |
| 6 | <span class="star" data-val="2"> |
| 7 | ★ |
| 8 | </span> |
| 9 | <span class="star" data-val="3"> |
| 10 | ★ |
| 11 | </span> |
| 12 | <span class="star" data-val="4"> |
| 13 | ★ |
| 14 | </span> |
| 15 | <span class="star" data-val="5"> |
| 16 | ★ |
| 17 | </span> |
| 18 | </div> |
| 19 | <span class="label"> |
| 20 | 3 / 5 |
| 21 | </span> |
| 22 | <div class="stars sm" style="--val:4"> |
| 23 | <span class="star" data-val="1"> |
| 24 | ★ |
| 25 | </span> |
| 26 | <span class="star" data-val="2"> |
| 27 | ★ |
| 28 | </span> |
| 29 | <span class="star" data-val="3"> |
| 30 | ★ |
| 31 | </span> |
| 32 | <span class="star" data-val="4"> |
| 33 | ★ |
| 34 | </span> |
| 35 | <span class="star" data-val="5"> |
| 36 | ★ |
| 37 | </span> |
| 38 | </div> |
| 39 | <span class="label small"> |
| 40 | 4 / 5 |
| 41 | </span> |
| 42 | </div> |
untitled.css
CSS
| 1 | * { |
| 2 | margin:0; |
| 3 | padding:0; |
| 4 | box-sizing:border-box |
| 5 | } |
| 6 | body { |
| 7 | font-family:system-ui, |
| 8 | sans-serif; |
| 9 | background:#0D0D0D; |
| 10 | padding:24px |
| 11 | } |
| 12 | .wrap { |
| 13 | display:flex; |
| 14 | align-items:center; |
| 15 | justify-content:center; |
| 16 | gap:16px; |
| 17 | padding:24px |
| 18 | } |
| 19 | .stars { |
| 20 | display:flex; |
| 21 | gap:4px; |
| 22 | direction:rtl |
| 23 | } |
| 24 | .star { |
| 25 | font-size:28px; |
| 26 | cursor:pointer; |
| 27 | color:#333; |
| 28 | transition:color .2s |
| 29 | } |
| 30 | .star:hover, |
| 31 | .star:hover~.star { |
| 32 | color:#EAB308 |
| 33 | } |
| 34 | .stars:not(:hover) .star { |
| 35 | color:var(--c) |
| 36 | } |
| 37 | @supports(color:color-mix(in srgb, |
| 38 | red, |
| 39 | blue)) { |
| 40 | .stars:has(.star:nth-child(1):hover) { |
| 41 | --c:#EAB308 |
| 42 | } |
| 43 | .star:nth-child(-n+3) { |
| 44 | color:#EAB308 |
| 45 | } |
| 46 | .stars:not(:hover) .star:nth-child(-n+3) { |
| 47 | color:#EAB308 |
| 48 | } |
| 49 | .stars:not(:hover) .star:nth-child(n+4) { |
| 50 | color:#333 |
| 51 | } |
| 52 | } |
| 53 | .label { |
| 54 | font-size:12px; |
| 55 | color:#808080 |
| 56 | } |
| 57 | .sm .star { |
| 58 | font-size:20px |
| 59 | } |
| 60 | .small { |
| 61 | font-size:11px; |
| 62 | color:#525252 |
| 63 | } |
untitled.js
JavaScript
| 1 | document.querySelectorAll('.stars').forEach(wrap=>{wrap.addEventListener('click',e=>{const star=e.target.closest('.star');if(!star)return;const val=star.dataset.val;wrap.style.setProperty('--val',val);wrap.querySelectorAll('.star').forEach((s,i)=>{s.style.color=i<val?'#EAB308':'#333'});const label=wrap.parentElement.querySelector('.label');if(label)label.textContent=val+' / 5'})}) |
Range Slider
Custom styled range slider with value display.
slider
Live
untitled.html
HTML
| 1 | <div class="wrap"> |
| 2 | <div class="slider-group"> |
| 3 | <label class="label"> |
| 4 | Volume |
| 5 | </label> |
| 6 | <input type="range" min="0" max="100" value="65" class="range" /> |
| 7 | <span class="val" id="vol"> |
| 8 | 65 |
| 9 | </span> |
| 10 | </div> |
| 11 | <div class="slider-group"> |
| 12 | <label class="label"> |
| 13 | Brightness |
| 14 | </label> |
| 15 | <input type="range" min="0" max="100" value="80" class="range green" /> |
| 16 | <span class="val" id="bri"> |
| 17 | 80 |
| 18 | </span> |
| 19 | </div> |
| 20 | <script> |
| 21 | document.querySelectorAll('.range').forEach(r=>{r.addEventListener('input',()=>{const v=r.nextElementSibling;if(v)v.textContent=r.value})}) |
| 22 | </script> |
| 23 | </div> |
untitled.css
CSS
| 1 | * { |
| 2 | margin:0; |
| 3 | padding:0; |
| 4 | box-sizing:border-box |
| 5 | } |
| 6 | body { |
| 7 | font-family:system-ui, |
| 8 | sans-serif; |
| 9 | background:#0D0D0D; |
| 10 | padding:24px |
| 11 | } |
| 12 | .wrap { |
| 13 | max-width:380px; |
| 14 | margin:16px auto; |
| 15 | display:flex; |
| 16 | flex-direction:column; |
| 17 | gap:20px; |
| 18 | padding:16px |
| 19 | } |
| 20 | .slider-group { |
| 21 | display:flex; |
| 22 | align-items:center; |
| 23 | gap:12px |
| 24 | } |
| 25 | .label { |
| 26 | font-size:12px; |
| 27 | color:#A0A0A0; |
| 28 | min-width:70px |
| 29 | } |
| 30 | .range { |
| 31 | -webkit-appearance:none; |
| 32 | appearance:none; |
| 33 | flex:1; |
| 34 | height:6px; |
| 35 | border-radius:3px; |
| 36 | background:#222; |
| 37 | outline:none |
| 38 | } |
| 39 | .range::-webkit-slider-thumb { |
| 40 | -webkit-appearance:none; |
| 41 | width:18px; |
| 42 | height:18px; |
| 43 | border-radius:50%; |
| 44 | background:#00FF41; |
| 45 | cursor:pointer; |
| 46 | border:2px solid #0D0D0D; |
| 47 | box-shadow:0 0 0 1px #00FF41 |
| 48 | } |
| 49 | .range.green::-webkit-slider-thumb { |
| 50 | background:#22C55E; |
| 51 | box-shadow:0 0 0 1px #22C55E |
| 52 | } |
| 53 | .val { |
| 54 | font-size:12px; |
| 55 | color:#808080; |
| 56 | min-width:28px; |
| 57 | text-align:right; |
| 58 | font-weight:600 |
| 59 | } |
untitled.js
JavaScript
| 1 | document.querySelectorAll('.range').forEach(r=>{const v=r.nextElementSibling;if(v){r.addEventListener('input',()=>v.textContent=r.value)}}) |
File Upload
Drag-and-drop file upload zone with button alternative.
file-upload
Live
untitled.html
HTML
| 1 | <div class="wrap"> |
| 2 | <div class="drop-zone"> |
| 3 | <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="color:#525252;margin-bottom:8px"> |
| 4 | <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> |
| 5 | <polyline points="17 8 12 3 7 8"/> |
| 6 | <line x1="12" y1="3" x2="12" y2="15"/> |
| 7 | </svg> |
| 8 | <span class="dz-text"> |
| 9 | Drop files here or |
| 10 | <span class="dz-link"> |
| 11 | browse |
| 12 | </span> |
| 13 | </span> |
| 14 | <span class="dz-hint"> |
| 15 | PNG, JPG, PDF up to 10MB |
| 16 | </span> |
| 17 | </div> |
| 18 | <button class="upload-btn"> |
| 19 | Choose File |
| 20 | </button> |
| 21 | </div> |
untitled.css
CSS
| 1 | * { |
| 2 | margin:0; |
| 3 | padding:0; |
| 4 | box-sizing:border-box |
| 5 | } |
| 6 | body { |
| 7 | font-family:system-ui, |
| 8 | sans-serif; |
| 9 | background:#0D0D0D; |
| 10 | padding:24px |
| 11 | } |
| 12 | .wrap { |
| 13 | max-width:380px; |
| 14 | margin:16px auto; |
| 15 | display:flex; |
| 16 | flex-direction:column; |
| 17 | gap:12px; |
| 18 | align-items:center; |
| 19 | padding:8px |
| 20 | } |
| 21 | .drop-zone { |
| 22 | width:100%; |
| 23 | padding:28px 20px; |
| 24 | border:2px dashed #333; |
| 25 | border-radius:10px; |
| 26 | display:flex; |
| 27 | flex-direction:column; |
| 28 | align-items:center; |
| 29 | justify-content:center; |
| 30 | cursor:pointer; |
| 31 | transition:all .3s |
| 32 | } |
| 33 | .drop-zone:hover { |
| 34 | border-color:#00FF41; |
| 35 | background:rgba(0, |
| 36 | 255, |
| 37 | 65, |
| 38 | .03) |
| 39 | } |
| 40 | .dz-text { |
| 41 | font-size:13px; |
| 42 | color:#808080 |
| 43 | } |
| 44 | .dz-link { |
| 45 | color:#00FF41; |
| 46 | cursor:pointer; |
| 47 | text-decoration:underline |
| 48 | } |
| 49 | .dz-hint { |
| 50 | font-size:10px; |
| 51 | color:#525252; |
| 52 | margin-top:4px |
| 53 | } |
| 54 | .upload-btn { |
| 55 | padding:10px 24px; |
| 56 | border-radius:8px; |
| 57 | font-size:12px; |
| 58 | font-weight:600; |
| 59 | font-family:inherit; |
| 60 | border:1px solid #2A2A3E; |
| 61 | background:#1A1A2E; |
| 62 | color:#E0E0E0; |
| 63 | cursor:pointer; |
| 64 | transition:all .2s |
| 65 | } |
| 66 | .upload-btn:hover { |
| 67 | background:#22223A; |
| 68 | border-color:#3A3A4E |
| 69 | } |
Input Groups
Inputs with attached icons, buttons, and prefix/suffix text.
input-group
Live
untitled.html
HTML
| 1 | <div class="wrap"> |
| 2 | <div class="i-group"> |
| 3 | <span class="i-prefix"> |
| 4 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| 5 | <circle cx="11" cy="11" r="8"/> |
| 6 | <line x1="21" y1="21" x2="16.65" y2="16.65"/> |
| 7 | </svg> |
| 8 | </span> |
| 9 | <input class="i-input" type="text" placeholder="Search..." /> |
| 10 | </div> |
| 11 | <div class="i-group"> |
| 12 | <input class="i-input" type="text" placeholder="Enter domain" /> |
| 13 | <button class="i-suffix"> |
| 14 | Check |
| 15 | </button> |
| 16 | </div> |
| 17 | <div class="i-group"> |
| 18 | <span class="i-prefix"> |
| 19 | $ |
| 20 | </span> |
| 21 | <input class="i-input" type="text" placeholder="0.00" /> |
| 22 | <span class="i-suffix"> |
| 23 | USD |
| 24 | </span> |
| 25 | </div> |
| 26 | </div> |
untitled.css
CSS
| 1 | * { |
| 2 | margin:0; |
| 3 | padding:0; |
| 4 | box-sizing:border-box |
| 5 | } |
| 6 | body { |
| 7 | font-family:system-ui, |
| 8 | sans-serif; |
| 9 | background:#0D0D0D; |
| 10 | padding:24px |
| 11 | } |
| 12 | .wrap { |
| 13 | display:flex; |
| 14 | flex-direction:column; |
| 15 | gap:12px; |
| 16 | max-width:360px; |
| 17 | margin:16px auto |
| 18 | } |
| 19 | .i-group { |
| 20 | display:flex; |
| 21 | align-items:stretch; |
| 22 | border-radius:8px; |
| 23 | overflow:hidden; |
| 24 | border:1.5px solid #2A2A3E; |
| 25 | background:#0A0A0A; |
| 26 | transition:border-color .2s |
| 27 | } |
| 28 | .i-group:focus-within { |
| 29 | border-color:#00FF41; |
| 30 | box-shadow:0 0 0 3px rgba(0, |
| 31 | 255, |
| 32 | 65, |
| 33 | .1) |
| 34 | } |
| 35 | .i-input { |
| 36 | flex:1; |
| 37 | padding:9px 12px; |
| 38 | font-size:13px; |
| 39 | font-family:inherit; |
| 40 | background:transparent; |
| 41 | border:none; |
| 42 | color:#E0E0E0; |
| 43 | outline:none |
| 44 | } |
| 45 | .i-input::placeholder { |
| 46 | color:#525252 |
| 47 | } |
| 48 | .i-prefix, |
| 49 | .i-suffix { |
| 50 | display:flex; |
| 51 | align-items:center; |
| 52 | padding:0 10px; |
| 53 | font-size:13px; |
| 54 | color:#808080; |
| 55 | background:rgba(255, |
| 56 | 255, |
| 57 | 255, |
| 58 | .02); |
| 59 | white-space:nowrap |
| 60 | } |
| 61 | .i-prefix { |
| 62 | border-right:1px solid #2A2A3E; |
| 63 | padding-right:10px |
| 64 | } |
| 65 | .i-suffix { |
| 66 | border-left:1px solid #2A2A3E; |
| 67 | font-size:11px; |
| 68 | font-weight:600; |
| 69 | cursor:pointer; |
| 70 | background:transparent; |
| 71 | border-top:none; |
| 72 | border-right:none; |
| 73 | border-bottom:none; |
| 74 | font-family:inherit; |
| 75 | color:#00FF41; |
| 76 | transition:all .2s |
| 77 | } |
| 78 | .i-suffix:hover { |
| 79 | background:rgba(0, |
| 80 | 255, |
| 81 | 65, |
| 82 | .08) |
| 83 | } |