Headless UI; iletişim kutuları, liste kutuları ve anahtarlar için stilsiz React etkileşim bileşenleri sunar. Bu kılavuz, belge paylaşımı akışını örnek alarak yerel odak, seçim ve açılır pencere davranışlarını sıradan CSS ile bir araya getirmeye odaklanır.
OpenDesign Kılavuzu9 Bölüm27 Başvuru KaydıÖzgün Uygulama Kılavuzu
01
Headless UI Tasarım Sistemi Arayüz Önizlemesi
Paylaşım iletişim kutusunu açın, bir e-posta adresini doğrulayın, klavyeyle izin seçin, yerel bir davet ekleyin ve herkese açık bağlantı durumunu değiştirin.
React 19 içeren yerel @headlessui/react 2.2.10. Tüm renkler, tipografi, boşluklar ve düzen, resmî Headless UI belirteçleri değil, özgün örnek CSS değerleridir. · Uygulama başvurusu
02
Headless UI Tasarım Sistemi Tasarım Başvuru Kaynağı
Headless UI için resmî bir görsel belirteç ölçeği yoktur. Aşağıdaki tüm sayısal ve renk değerleri bu OpenDesign örneğine aittir; bağlantılı belgeler davranışları ve durum API’lerini açıklar. Test edilen paket: @headlessui/react 2.2.10.
Headless UI Tasarım Sistemi: Ekranı Biçimlendirmeden Önce Davranışı Kurgulayın
Ekranı Biçimlendirmeden Önce Davranışı Kurgulayın
Dialog, Listbox ve Switch farklı etkileşimleri modeller. Dialog, sınırları belirli bir görevi ve bu görevin odak bağlamını yönetir; Listbox, onaylanmış tek bir seçimi temsil eder; Switch ise bağımsız bir Boole ayarını temsil eder. Bunları bir araya getirmek, durumlarını birleştirmeyi gerektirmez: davet taslağı, seçili izin ve herkese açık bağlantı ayarı birbirinden ayrı kalır.
Örnek, doğrudan DialogPanel ve ListboxButton gibi içe aktarımlarla React bileşen API öğesini kullanır. İletişim kutusunun içinde bir liste kutusu bulunduğundan açılır listenin konumlandırılması ve kapatılması, mevcut bir katmanın içinde de düzgün çalışmalıdır. Belge panelini ve erişim özetini çevresine eklemeden önce bu bileşimi kurun. Sağlanan CSS, bu yerel bileşenler için eksiksiz bir görünüm örneğidir.
Headless UI Tasarım Sistemi: Dialog, Listbox ve Switch Bileşenlerinin Yapısı
Katman sınırlarını bilinçli belirleyin
DialogBackdrop, sabit konumlandırma kapsayıcısının kardeşidir. DialogPanel, bu kapsayıcının içindeki sınırları belirli beyaz paneldir; görünüm alanının tamamını kaplamaz. DialogTitle görevi adlandırır, Description ise kapatıldığında ne olacağını açıklar. Yerel girişte data-autofocus bulunur ve Headless UI, çevreleyen iletişim kutusunun odak davranışını yönetir.
Liste kutusu bir değer seçer; anahtar bir Boole değerini değiştirir
ListboxButton seçili rolü gösterir. ListboxOptions, açılır listenin tetikleyiciyle hizalanması için anchor="bottom" ve --button-width değişkenine dayalı bir genişlik kullanır. Her ListboxOption öğesinin kararlı bir metin değeri vardır. Yerel data-focus ve data-selected öznitelikleri, birbirinden farklı iki CSS görünümünü belirler.
Herkese açık bağlantı Switch’i, etiketsiz, görsel amaçlı bir kapsül yerine Label içeren Field içinde yer alır. Açık duruma geçtiğinde erişilebilir denetimin durumu ve görünür açıklama cümlesi değişir. Yerel davet, herkese açık bağlantı ayarından bağımsızdır.
Headless UI Tasarım Sistemi: Seçimi, Kapatmayı ve Yerel Onayı Birbirinden Ayırma
Burada kullanılan durum modeli
Pencere açıldığında alıcı taslağı sıfırlanır ve Can view rolü seçilir. İptal, Escape ve pencere dışına tıklama iletişim kutusunu kapatır; bunların hiçbiri alıcı eklemez. Gönderim sırasında e-posta doğrulanır, listede zaten bulunan adres reddedilir, ardından davet belleğe eklenir ve iletişim kutusu kapatılır. Onay mesajı, e-posta gönderilmediğini açıkça belirtir.
İzin listbox’ı taslak rolü değiştirir; mevcut alıcının erişimini değiştirmez. Herkese açık bağlantı durumu ayrı bir boolean değerini değiştirir. Böylece her kapatma olayının izinleri kaydettiğini varsayma gibi yaygın bir hatadan kaçınılır.
Geçersiz giriş: İletişim kutusunu açık tutun ve hatayı e-posta alanıyla ilişkilendirin.
Yinelenen alıcı: Mevcut erişim listesini değiştirmeyin.
Yerel davet kabul edildi: Listeyi güncelleyin ve sonucun yalnızca önizleme amaçlı olduğunu bildirin.
Headless UI Tasarım Sistemi: Seçim Durumunu ve Klavye Konumunu Birbirinden Bağımsız Biçimde Gösterme
Seçim Durumunu ve Klavye Konumunu Birbirinden Bağımsız Biçimde Gösterme
ListboxOption, farklı koşulları belirtmek için data-focus ve data-selected özelliklerini sunar. Odaktaki seçenek soluk vurgu görünümü alırken, seçili izin onay işaretiyle gösterilir. Böylece klavye kullanan kişiler, mevcut seçimi gözden kaçırmadan seçenekler arasında gezinebilir.
Aynı ayrım açılır pencerenin dışında da geçerlidir: hata rolü geçersiz alıcı alanına, Switch’in seçili görünümü ise herkese açık bağlantı erişimine aittir. CSS kapsamını, anlattığı bileşen durumuyla sınırlı tutun. Odaklanmış veya seçili her öğeyi aynı şekilde renklendiren geniş kapsamlı bir seçici, bu farklı anlamları belirsizleştirir.
Headless UI Tasarım Sistemi: Belge Kimliğini ve İzinleri Okunur Kılma
Yeniden üretilebilen tipografi ayarları
Belge başlığı 28px ve 1.25 satır yüksekliğindeyken iletişim kutusu başlığı 20px ve 1.3 satır yüksekliğindedir. Gövde metni, platformun sistem yazı tipinde 16px/1.5 boyutundadır. Alan etiketleri ve yardımcı metin 14px’tir. Bu kesin değerler style.css dosyasındadır; okuyucuların bunları ekran görüntüsünden tahmin etmesi gerekmez.
Erişim listesindeki e-posta için overflow-wrap:anywhere kullanılır; böylece adres sütununun genişlemesine yol açamaz. İzin ayrı bir satırda gösterilir. İletişim kutusunda, davet eklenirken belgenin tam başlığı ve seçili izin görünür durumdadır.
Headless UI Tasarım Sistemi: Kaplama Konumlandırmasını ve Duyarlı Düzeni Eksiksiz Sağlama CSS
Düzen, bir ön koşulun arkasına gizlenmemiştir
Sağlanan stil sayfası kaplamanın tamamını içerir: sabit inset konumlandırması, arka plan opaklığı, z-index, maksimum genişlik, dikey kaydırma ve iç boşluklar. 700px altında belge panelleri alt alta gelir ve iletişim kutusunun iç boşluğu azalır. Tailwind derlemesi varsayılmamıştır.
Konumlandırma kapsayıcısını DialogPanel’den ayrı tutun. Kapsayıcı, kullanılabilir görünüm alanının tamamını kaplar ve kenar boşluğu sağlar; panel ise sınırlandırılmış içerik yüzeyini sunar. Alıcı alanının, izin seçiminin ve kapatma eylemlerinin dar ekranlarda da erişilebilir kalması için uzun iletişim kutusu içeriğine kaydırılabilir bir alan ekleyin.
Headless UI Tasarım Sistemi: Yerel Durum ve Odak Davranışını Kullanma
Klavye ile doğrulama
İletişim kutusunu açın ve başlangıçta alıcının odakta olduğunu doğrulayın. Tab tuşuyla izin tetikleyicisine ulaşın; bir rol seçmek için ok tuşlarını ve Enter’ı kullanın. Escape, yerel bileşenin geçerli odak bağlamına göre etkin açılır pencereyi veya iletişim kutusunu kapatır. İletişim kutusu kapandıktan sonra odak, paylaşım tetikleyicisine dönmelidir.
E-posta alanı, özel hata mesajı için aria-invalid ve aria-describedby kullanır. Seçenekteki onay işareti dekoratiftir; çünkü seçim semantiğini Listbox sağlar. Herkese açık bağlantı denetiminin görünür bir Label’ı vardır ve açıklama cümlesi anahtar değiştirildikten sonra da görünür kalır. Azaltılmış hareket tercihi CSS, dekoratif anahtar düğmesi geçişini kaldırır.
Headless UI Tasarım Sistemi: Tutarlı Biçimde Tek Bir React API Sürümünü Kullanma
Tutarlı Biçimde Tek Bir React API Sürümünü Kullanma
Eksiksiz React girişi, kaplama geometrisini ve durum seçicilerini sağlayan stil sayfasıyla birlikte App’i işler. DialogPanel, DialogBackdrop, ListboxButton ve ListboxOptions, doğrulanmış 2.2.10 örneğinde doğrudan içe aktarılır. Eski bileşik bileşen yazımları, bu bileşime karıştırılmak yerine bilinçli biçimde taşınmalıdır.
ListboxOptions, seçenekleri tetikleyiciyle hizalamak için anchor="bottom" ve yerel --button-width değişkenini kullanır. Bu ayrıntılar entegrasyonun parçasıdır, isteğe bağlı süslemeler değildir: konumlandırma kapsayıcısını kaldırmak veya genişliği ilgisiz sabit bir değerle değiştirmek, kapalı alan doğru görünse bile iç içe açılır pencerenin çalışmasını bozabilir.
Örneklerdeki App.jsx ve style.css dosyalarını kaydedin, ardından main.jsx dosyasını gösterildiği gibi değiştirin.
Geliştirme sunucusunu çalıştırın; iletişim kutusunun kapatılmasını, izin seçimini ve yinelenen e-posta durumundan kurtarmayı sınayın.
Headless UI Tasarım Sistemi: Gerçek Bir İzin Etkileşimini İnceleme
Kabul koşulları
Yararlı bir incelemeye boş bir bileşen galerisinden değil, bir belge ve mevcut bir alıcıyla başlayın. Can comment rolüyle geçerli bir davet ekleyin, ardından iletişim kutusunu yeniden açıp aynı e-postayı deneyin. Özgün satırın korunduğunu ve yinelenen adres için belirgin bir hata gösterildiğini doğrulayın. Herkese açık bağlantıyı açın ve bağımsız açıklamasını inceleyin.
Örnek yalnızca yerel arayüz durumunu gösterir. Alıcı araması veya paylaşım arka ucu sağlamaz. Gerçek bir hizmete bağlamak, bekleyen/hata yanıtı modeli ve eşzamanlı izin değişiklikleri için açık bir politika gerektirir; yerel temel bileşenler yetkilendirme kurallarını belirlemez.
Headless UI Tasarım Sistemi Bileşen Örnekleri
App.jsx — yerel paylaşım bileşimi
Tam denetimli Dialog, izin Listbox’ı, etiketli Switch ve yerel doğrulama.
jsx
import React, { useState } from 'react';
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Description, Listbox, ListboxButton, ListboxOptions, ListboxOption, Switch, Field, Label } from '@headlessui/react';
import './style.css';
const roles = ['Can view', 'Can comment', 'Can edit'];
export default function App() {
const [open,setOpen]=useState(false),[role,setRole]=useState(roles[0]),[publicLink,setPublicLink]=useState(false),[email,setEmail]=useState(''),[error,setError]=useState(''),[notice,setNotice]=useState('');
const [people,setPeople]=useState([{email:'[email protected]',role:'Can edit'}]);
const start=()=>{setEmail('');setError('');setRole(roles[0]);setOpen(true)};
const submit=e=>{e.preventDefault();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)){setError('Enter a complete email address.');return;}if(people.some(p=>p.email===email)){setError('This person already appears in the access list.');return;}setPeople([...people,{email,role}]);setOpen(false);setNotice('Invitation added to this local example only. No email was sent.');};
return <main className='sharing-shell'>
<header><div><span className='eyebrow'>HEADLESS UI / CUSTOM SURFACE</span><h1>Quarterly brief</h1><p className='muted'>Research · Updated today</p></div><button className='primary' onClick={start}>Share document</button></header>
<div className='document-grid'><article className='paper'><span className='tag'>WORKING DOCUMENT</span><h2>Make the next decision clearer.</h2><p>Three research priorities for the coming quarter, shared with the people who need to review them.</p><div className='rule'/><h3>01 / Understand the handoff</h3><p>Compare where context is lost between research notes and the next product decision.</p><h3>02 / Review access deliberately</h3><p>Keep editing rights separate from the ability to read a document.</p></article>
<aside className='access'><h2>People with access</h2><p className='muted'>The list below is local demonstration state.</p>{people.map((p,i)=><div className='person' key={p.email}><span className='avatar'>{i===0?'AK':'+'}</span><div><strong>{p.email}</strong><small>{p.role}</small></div></div>)}<Field className='link-setting'><Label>Public link</Label><Switch checked={publicLink} onChange={setPublicLink} className='switch'><span /></Switch></Field><p className='link-detail'>{publicLink?'Anyone with the example link could view this document. This changes only the local preview.':'Restricted to the named people in this example.'}</p><p role='status' className='notice'>{notice}</p></aside></div>
<Dialog open={open} onClose={()=>setOpen(false)} className='dialog-root'><DialogBackdrop className='backdrop'/><div className='dialog-position'><DialogPanel className='dialog-panel'><DialogTitle as='h2'>Share quarterly brief</DialogTitle><Description className='muted'>Choose one recipient and a permission. Closing discards this invitation draft.</Description><form noValidate onSubmit={submit}><label className='field-label' htmlFor='recipient'>Recipient email</label><input id='recipient' data-autofocus type='email' value={email} onChange={e=>{setEmail(e.target.value);setError('')}} aria-invalid={!!error} aria-describedby={error?'email-error':undefined} placeholder='[email protected]'/>{error&&<p id='email-error' role='alert' className='error'>{error}</p>}<Listbox value={role} onChange={setRole}><Label className='field-label'>Permission</Label><ListboxButton className='role-button'>{role}<span aria-hidden='true'>⌄</span></ListboxButton><ListboxOptions anchor='bottom' className='role-options'>{roles.map(value=><ListboxOption key={value} value={value} className='role-option'><span>{value}</span><span className='selected' aria-hidden='true'>✓</span></ListboxOption>)}</ListboxOptions></Listbox><p className='muted permission-help'>{role==='Can edit'?'May change document content in this example.':role==='Can comment'?'May add comments without editing content.':'May read the document without changing it.'}</p><div className='dialog-actions'><button type='button' onClick={()=>setOpen(false)}>Cancel</button><button className='primary' type='submit'>Add invitation</button></div></form></DialogPanel></div></Dialog>
<footer>Native Dialog, Listbox, and Switch behavior. Original CSS below supplies all visual values; Headless UI does not ship this palette.</footer>
</main>;
}
App.jsx için gereken tüm görsel belirteçler, durum seçicileri, kaplama yerleşimi ve duyarlı düzen kuralları. Bunlar örnek tercihleridir; Headless UI için resmî varsayılanlar değildir.
data-focus ve data-selected özelliklerini birbirinden ayrı tutun.
Tailwind ön koşulu yoktur.
04
Headless UI tasarım sistemi Yapay Zekâ Ajanı Kurulumu
Uygulamaya Başlangıç Noktası
Tam stil ve bileşen dosyaları yukarıda sağlanmıştır. Seçilen Vite sürümünün desteklediği Node sürümünü gerektirir. Demo, aynı yerel bileşen paketini yerel olarak paketler.
text
# Terminal
npm create vite@latest sharing-demo -- --template react
cd sharing-demo
npm install
npm install @headlessui/[email protected]
# Save the complete examples as src/App.jsx and src/style.css.
# Replace src/main.jsx with:
import React from 'react';
import {createRoot} from 'react-dom/client';
import App from './App';
createRoot(document.getElementById('root')).render(<App/>);
# Terminal
npm run dev
Projeniz İçin Bir İstem
Ajan İstemi
Sağlanan Headless UI App.jsx ve style.css dosyalarını kullanın. Yerel Dialog, Listbox ve Switch bileşimini koruyun; taslak izinleri onaylanmış erişimden ayrı tutun ve tüm odak ile seçili durum seçicilerini koruyun. Yerel davet ekleme işlemini, bekleyen ve başarısız durumları da içeren yetkili API ile değiştirin; onClose sırasında asla kaydetmeyin. Özel CSS paletini resmî bir Headless UI teması olarak tanımlamayın.
05
Headless UI Tasarım Sistemi Kaynakları ve Kapsamı
Özgün OpenDesign görsel yapılandırması ve yerel Headless UI 2.2.10 davranışını kullanan React örneği. CSS değerleri açıkça uygulamaya özgü tercihlerdir; Headless UI bu temayı sağlamaz. Davetler ve izin değişiklikleri yalnızca yerel önizleme belleğinde tutulur.