Gerçek anlamsal tasarım belirteçleri, web yazı tipi ve boşluk ölçekleri, denetimli sekmeler ve doğrulanmış erişim iletişim kutusuyla bir Fluent UI React v9 çalışma alanı oluşturun. Tema değerlerini karşılaştırın, bileşen anatomisini inceleyin ve eksiksiz React örneğini kullanmadan önce yerel bir etkileşimi deneyin.
OpenDesign Kılavuzu9 Bölüm64 referans girdisiÖzgün Uygulama Kılavuzu
01
Microsoft Fluent 2 Tasarım Sistemi Arayüz önizlemesi
Brief ve Kişiler arasında geçiş yapın, web temasını açıp kapatın ve başarısız, yinelenen ve başarılı yerel ekleme işlemlerini deneyin. Davetiye gönderilmez.
Fluent UI React v9 web belirteçlerinin ve belgelenmiş Field, TabList ve Dialog bileşen yapılarının bağımsız HTML/CSS yorumu. Yerel Fluent bileşen çalışma zamanı değildir. · Uygulama başvurusu
02
Microsoft Fluent 2 Tasarım Sistemi Tasarım referansı
Fluent UI React v9 web referansları. Genel sayısal değerler, bağlantısı verilen React belirteç kaynaklarından alınmıştır. Aşağıdaki renk değerleri, alias/lightColor.ts içindeki güncelliğini yitirmiş örnek HEX yorumlarından değil, brandWeb ile açık tema takma ad eşlemesi kullanılarak elde edilmiştir. Koyu renkler ve Teams temaları ayrı eşlemelere sahiptir. Web sitesi tipografi tablosunda Subtitle1 şu anda 20/26 olarak listeleniyor; React fonts.ts kaynağında 20/28 kullanılıyor ve bu uygulama da onu izliyor. Kaynaklar 3 Ekim 2026 tarihinde kontrol edilmiştir; kullandığınız sürüm için kilit dosyanızda kayıtlı değerleri doğrulayın.
Microsoft Fluent 2 Tasarım Sistemi ve React v9 uygulaması
Web uygulamasını açıkça seçin
Fluent 2, platformlar arası bir tasarım dilidir. Bu kılavuz daha dar kapsamlı ve uygulanabilir bir tercih yapar: yakınsama sağlayan React v9 kitaplığı @fluentui/react-components. Kök bileşen FluentProvider'dır ve bu örnekte webLightTheme veya webDarkTheme kullanılır. Teams teması ayrı bir markalı yapılandırmadır; Teams'te kullanılan tanıdık mor renk, varsayılan mavi web renk dizisi değildir. Yerel Windows, iOS ve Android kaynakları da birbirinin yerine kullanılamaz React paketleridir.
Aşağıdaki eksiksiz örnek, bir proje özetini ve yerel bir erişim listesini görüntüler. Hiyerarşi için Title3 ve Body1'i, iki görünüm için TabList ve Tab'ı, erişim kararı için de Field, Input ve Select içeren Dialog'u bir araya getirir. Kod, gerçek bir kişiye davet gönderdiğini iddia etmez. Bu ayrım, paylaşım hizmeti varmış gibi davranmadan bileşen davranışını göstermeyi sağlar.
FluentProvider, tema değişkenlerini kendi alt ağacına ekler. tokens.colorNeutralBackground2 ile biçimlendirilen özel bir yüzey seçili temayı izlerken beyaza sabitlenmiş bir yüzey izlemez. Tema gerektiren bileşenlerin üstünde sağlayıcıyı konumlandırın. Kitaplık, bileşenlerini biçimlendirme çalışma zamanı aracılığıyla stillendirir; örnek, var olmayan bir fluent.css dosyasını içe aktarmanızı istemez.
Microsoft Fluent 2 Tasarım Sistemi: Anlamsal renkler ve gerçek web marka renk dizisi
Takma adları seçilen markaya göre çözümleyin
Açık tema takma adı kaynağı, colorBrandBackground değerini brand[80] olarak atar. Web teması, 80. adımı #0f6cbd olan brandWeb değerini sağlar. Bu nedenle bu örnekte çözümlenen birincil dolgu rengi #0f6cbd olur. Takma ad dosyasında eski örnek amaçlı onaltılık renk yorumları bulunur; bu yorumları güncel web paleti olarak değerlendirmek yanlış sonuç verir. Değeri yorum değil, kaynak ilişkisi belirler.
Özet için colorNeutralForeground1, destekleyici etiketler için colorNeutralForeground2 ve çevredeki çalışma alanı için colorNeutralBackground3 kullanın. Birincil bir eylemde colorBrandBackground ile colorNeutralForegroundOnBrand kullanılabilir. Tema değiştiğinde de rol adları sözleşme niteliğini korur. Kişinin izni metinle belirtilir; mavi kenarlık veya mavi sekme göstergesi yetkilendirmeyi değil, seçimi ifade eder.
Koyu modda ana yüzey #292929, birincil metin ise #ffffff olarak çözülür. Birincil marka dolgusunda 70. adım, bağlantı ön planında ise 100. adım kullanılır. Bu nedenle önizleme, yalnızca sayfa arka planını tersine çevirmek yerine belirteç rollerini birlikte değiştirir. Erişim iletişim kutusunu, seçili sekmeyi ve soluk meta verileri geçiş yaparak karşılaştırın: hepsi aynı tema sınırının parçasıdır.
Microsoft Fluent 2 Tasarım Sistemi Tipografisi: Metin rollerini çalışma zamanı belirteçleriyle eşleştirin
Boyut ve satır yüksekliği değerlerini birlikte kullanın
Body1, 14px boyut ve 20px satır yüksekliğiyle temel okuma stilidir. Caption1 12/16, Subtitle2 yarı kalın ağırlıkla 16/22, Title3 ise yarı kalın ağırlıkla 24/32 değerlerini kullanır. Bu eşleşmeler, gelişigüzel ara boyutlara gerek kalmadan başlık, etiket ve gövde metni arasında belirgin bir hiyerarşi oluşturur. Yukarıdaki tabloda daha büyük adımlar da başvuru amacıyla yer alır; ancak kompakt bir çalışma alanında ekran boyutunda bir başlık gerekmez.
React belirteç kaynağı ile tasarım web sitesi, işe yarar bir ayrıntıda şu anda birbiriyle uyuşmuyor: çalışma zamanı kaynağında fontSizeBase500 / lineHeightBase500 20/28 değerindeyken web tasarım çizelgesinde Subtitle1 20/26 olarak listeleniyor. Bu örnek, çalışma zamanı değerlerini kullanır. Bu tür farklılıkları, makete uyan sayıları fark ettirmeden birleştirmek yerine sürüm ve uygulama kontrolü olarak değerlendirin.
Görsel hazır ayarın yanı sıra anlamsal HTML HTML kullanın: as="h1" olan Title3 bir başlıktır; yalnızca biçimlendirme uygulamak belge yapısını oluşturmaz. Temel yığın Segoe UI ile başlar ve platforma özgü yedek yazı tiplerini içerir. Ekran görüntüsünü eşleştirmek için özel bir yazı tipini pakete eklemeyin. Örnek, belgelenmiş yığını kullandığından gliflerin tam görünümü, görüntüleme cihazında yüklü yazı tiplerine bağlıdır.
Özel metinlerde typographyStyles.body1; aile, boyut, ağırlık ve satır yüksekliğini bir arada sunar. Yalnızca fontSizeBase300 değerini kopyalamak yerine bu birleşimi tercih edin. Erişim listesinde satır kaydırma destekli, okunaklı e-posta adresleri kullanılır; izin etiketi yarı kalın vurgulanır ancak sayfa başlığıyla yarışmaz.
Microsoft Fluent 2 Tasarım Sistemi: Çalışma alanında boşluk, şekil ve yoğunluk
React boşluk adları, tasarım aracındaki adlar değildir
Genel Fluent yerleşim yönergeleri, hizalama ayarlamaları için 2, 6 ve 10 dahil olmak üzere dört birimlik ritmi ve global boyut adlarını kullanır. React ise eksene özgü takma adlar sunar: spacingHorizontalS 8px, M 12px, L 16px ve XXL 24px değerindedir; dikey karşılıkları da bulunur. size120 değerini spacingHorizontal120 olarak çevirmeyin; React içinde böyle bir tanımlayıcı yoktur.
Bu düzenlemede panel dolgusu 24px, form alanları arasındaki boşluk 16px ve erişim satırları arasındaki boşluk 12px'tir. Sayfanın tam genişliği ve iki sütunlu düzen, resmî Fluent kesme noktaları değil, uygulamaya özgü kararlardır. Dar ekranlarda destekleyici ekip bağlamı özetin altına taşınırken iki sekme etiketi görünür kalır.
Microsoft Fluent 2 Tasarım Sistemi TabList: Seçim, paneller ve klavye davranışı
Seçili değer uygulama durumudur
Örnekte sekme durumu React içinde tutulur ve selectedValue özelliğine aktarılır. onTabSelect, durumu güncelleyen data.value değerini alır. Her Tab'ın kararlı bir değeri vardır ve aria-controls aracılığıyla paneline işaret eder. Her panelde role="tabpanel", aria-labelledby ve bir gizleme koşulu bulunur. Bileşen proje özetinizi veya erişim listenizi sağlamaz; bunlar açıkça oluşturulur.
API odaklanma ile seçimi birbirinden ayırır: selectTabOnFocus varsayılan olarak false değerindedir. Bağımsız örnekte Sol/Sağ ve Home/End tuşları odağı taşır; Enter veya Space odaktaki sekmeyi etkinleştirir. Bu elle etkinleştirme davranışı, kişinin beklenmedik bir içerik değişikliğini tetiklemeden sekme seçeneklerini incelemesini sağlar. Sekme etiketlerini kısa tutun; resmî yerleşim yönergelerine göre yatay sekmeler kendiliğinden satır kaydırmaz veya kaydırılabilir hâle gelmez.
Brief ve People, tek bir çalışma alanının birbiriyle yakından ilişkili iki görünümüdür; bu nedenle sekmeler uygundur. Kurumdaki tüm projelerin listesi ise başka bir sekme satırı değil, gezinme öğesidir. Kategoriler dar bir genişliğe sığmamaya başladığında taşma davranışını veya başka bir seçim desenini bilinçli olarak tasarlayın; etiketleri okunamaz hâle gelene kadar küçültmek duyarlı tasarım yaklaşımı değildir.
Microsoft Fluent 2 Tasarım Sistemi Field: Erişilebilir etiketler ve anlamlı doğrulama
Field, biçimlendirilmiş bir giriş alanından ibaret değildir
Field; etiket yuvasını, alt denetimini, isteğe bağlı bir ipucunu ve doğrulama iletisini bir araya getirir. Belgelenmiş denetim sözleşmesinde id, aria-labelledby, aria-describedby, aria-invalid ve aria-required bulunur. Fluent girişleri doğrudan alt öğe olabilir; özel denetimler, işleme işlevinin özelliklerini kullanabilir. Bu nedenle e-posta Input'unu Field ile sarmalamak, giriş alanının üstüne ilişkisiz metin koymaktan daha yararlıdır.
Örnekte validationState yalnızca geçersiz bir yerel gönderimden sonra error olur. Uygulama, bellekte tutulan listedeki e-posta biçimine uygun değerleri ve yinelenen kayıtları denetler. Girilen değeri korur ve validationMessage aracılığıyla düzeltme bilgisi sunar. Field bu sonucu gösterir ve ilgili girişle ilişkilendirir; adresin var olup olmadığına karar vermez, davet göndermez veya erişim izni vermez.
İzin Select'inde Can view ve Can edit seçenekleri bulunurken e-posta alanının kendi hata durumu vardır. Tek bir adres eksik diye iletişim kutusunun tamamını kırmızıya çevirmeyin. required, erişilebilir bir belirtim sağlar; sunucu yetkilendirmesinin veya doğrulamasının yerini tutmaz. Örnek formda, açık doğrulama mesajının tutarlı biçimde gösterilebilmesi için noValidate kullanılır; üretim ortamındaki bir hizmet isteği yeniden doğrulamalıdır.
Adres boş veya hatalı biçimlendirilmiş: iletişim kutusunu açık tutun ve kabul edilen biçimi açıklayın.
Adres yerel erişim listesinde zaten var: yeni bir satır eklemeden, yinelenen adrese özel bir mesaj gösterin.
Yeni ve geçerli adres: seçilen izni yerel olarak ekleyin, iletişim kutusunu kapatın ve yalnızca yerel kapsamda gerçekleşen sonucu doğru biçimde belirtin.
Microsoft Fluent 2 Tasarım Sistemi Dialog: desteklenen bileşen yapısı içinde form
Gönderme düğmesini formun içinde tutun
Bileşim; Dialog, DialogTrigger, DialogSurface, bir form öğesi ve DialogTitle, DialogContent ile DialogActions içeren DialogBody'den oluşur. Form, gövdeyi kapsar; böylece alt bilgideki gönderme düğmesi form gönderimine katılır. Cancel, type="button" olarak ayarlanır ve kapatma işlevi gören bir DialogTrigger içinde yer alır; aksi hâlde varsayılan düğme türü formu yanlışlıkla gönderebilir.
open ve onOpenChange, görünürlüğü denetler. disableButtonEnhancement, düğme davranışını zaten sağlayan bir Fluent Button'ın tetikleyici tarafından gereksiz yere yeniden geliştirilmesini önler. Başarı işleyicisi yalnızca yerel doğrulama başarılı olduktan sonra iletişim kutusunu kapatır. Doğrulama başarısız olursa aynı e-posta ve izin düzeltilebilmesi için korunur.
Bu kısa erişim kararı, varsayılan modal türünü kullanır. Modal olmayan bir iletişim kutusu arka planda çalışmaya izin verirken uyarı iletişim kutusu acil kararlar için tasarlanmıştır ve kapatma davranışı daha katıdır. Standart bir erişim formunun uyarı anlambilimine ihtiyacı yoktur. Resmî yönergeler, odağın iletişim kutusunun içinde olmasını ve kapatıldıktan sonra tetikleyiciye dönmesini önerir; bağımsız yorumlama, yerel dialog öğesini kullanır ve odağı tetikleyiciye açıkça geri verir.
Microsoft Fluent 2 Tasarım Sistemi Temayı bozmadan özel stiller kullanma
makeStyles ve anlamsal değişkenleri kullanın
Tam kodda, yüzeyler, boşluklar ve köşe yarıçapı için belirteçlerle makeStyles kullanılır. Bu, özel panelin sağlayıcıyla uyumlu kalmasını sağlar. Oluşturulan sınıf adı bir uygulama ayrıntısıdır; bu nedenle DevTools'tan alıp bir stil sayfasına kopyalamayın. Tema denemeleri için sağlayıcı temasını değiştirin ve yerel denetimlerle özel bölgeleri birlikte inceleyin.
Yayımlanmış süre basamağı 50ms ile 500ms arasındadır. HTML örneğinde, basit üzerine gelme değişiklikleri için 150ms değerindeki durationFast kullanılır ve prefers-reduced-motion altında bu geçişler kaldırılır. Bu, kullanılabilir bir belirteçten yararlanan uygulama tercihidir; her Fluent etkileşiminin 150ms sürmesi gerektiği anlamına gelmez. Hareket kaldırıldığında da içerik ve hata mesajları anlaşılır kalır.
Microsoft Fluent 2 Tasarım Sistemi Tam bileşimi yükleyip doğrulayın
Görev taslağı değil, çalıştırılabilir giriş noktası
Kurulum, root kimliğine sahip bir öğe içeren mevcut bir React uygulamasını hedefler. v9 paket takımını yükleyin, ardından JSX giriş noktasını uygulamaya ekleyin. İçe aktarımlar, sağlayıcı, bileşen ağacı, işleyiciler, durum ve kök oluşturma birlikte sunulmuştur. Harici bir paylaşım API hizmeti çağrılmaz. Başlangıç projeniz App'i zaten oluşturuyorsa ikinci bir kök oluşturmak yerine mevcut kök giriş noktasını koruyup App'i dışa aktarın.
Çözümlenen paket sürümünü kilit dosyasına kaydedin. Bağlantılı kaynak referansları, incelenen React v9 uygulamasını açıklar; master URL'leri ise zamanla değişebilir. Yükseltme yapmadan önce dışa aktarılan temayı ve burada kullanılan Field, TabList ve Dialog davranışlarının tam karşılığını karşılaştırın. Kurulum, v8 @fluentui/react bileşenlerini v9 örnekleriyle bilerek bir arada kullanmaz.
Mevcut React projesine @fluentui/react-components@9 yükleyin ve oluşturulan kilit dosyasını koruyun.
Sağlanan giriş noktasını, başlangıç teması olarak webLightTheme kullanılan tek bir FluentProvider altında oluşturun.
İletişim kutusunu açın, hatalı bir adres gönderin, adresi düzeltin ve eklenen kişinin yalnızca yerel durumda göründüğünü doğrulayın.
webDarkTheme ve klavye gezintisiyle de tekrarlayın; panel ilişkilendirmesini, hata duyurusunu ve odağın geri dönmesini doğrulayın.
Gerçek bir paylaşım ürününde bağlanması gerekenler
Yerel eklemenin yerine hizmeti kullanmadan önce hizmet sözleşmesini tanımlayın: kimliği doğrulanmış kullanıcı, hedef çalışma alanı, izin verilen izin değerleri, davet sonucu ve yinelenen kayıt davranışı. Hizmet isteği reddederse iletişim kutusunu açık tutun ve erişimin verildiğini erkenden iddia etmek yerine doğrulanmış sonucu gösterin. Bu kurallar Fluent kapsamı dışındadır; ancak kod, devri genel bir gönderme düğmesinin arkasına saklamak yerine açıkça belirtir.
Microsoft Fluent 2 Tasarım Sistemi Bileşen örnekleri
Tamamlanmış, bağımsız ve etkileşimli örnek
Belgenin tamamını index.html olarak kaydedip bir tarayıcıda açın. Fluent UI React v9 web belirteçlerinin ve belgelenmiş Field, TabList ve Dialog bileşen yapısının bağımsız bir HTML/CSS yorumlamasıdır. Yerel bir Fluent bileşen çalışma zamanı değildir.
html
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Fluent web workspace study</title><style>
:root{color-scheme:light;--colorNeutralForeground1:#242424;--colorNeutralForeground2:#424242;--colorNeutralForeground3:#616161;--colorNeutralBackground1:#fff;--colorNeutralBackground2:#fafafa;--colorNeutralBackground3:#f5f5f5;--colorNeutralStroke1:#d1d1d1;--colorNeutralStroke2:#e0e0e0;--colorBrandBackground:#0f6cbd;--colorBrandBackgroundHover:#115ea3;--colorBrandBackgroundPressed:#0c3b5e;--colorBrandBackground2:#ebf3fc;--colorBrandForegroundLink:#115ea3;--colorNeutralForegroundOnBrand:#fff;--borderRadiusMedium:4px;--borderRadiusXLarge:8px;--spacingHorizontalS:8px;--spacingHorizontalM:12px;--spacingHorizontalL:16px;--spacingHorizontalXXL:24px;--durationFast:150ms}
[data-theme=dark]{color-scheme:dark;--colorNeutralForeground1:#fff;--colorNeutralForeground2:#d6d6d6;--colorNeutralForeground3:#adadad;--colorNeutralBackground1:#292929;--colorNeutralBackground2:#1f1f1f;--colorNeutralBackground3:#141414;--colorNeutralStroke1:#666;--colorNeutralStroke2:#525252;--colorBrandBackground:#115ea3;--colorBrandBackgroundHover:#0f6cbd;--colorBrandBackground2:#082338;--colorBrandForegroundLink:#479ef5}
*{box-sizing:border-box}body{margin:0;background:var(--colorNeutralBackground3);color:var(--colorNeutralForeground1);font-family:'Segoe UI','Segoe UI Web (West European)',-apple-system,BlinkMacSystemFont,Roboto,'Helvetica Neue',sans-serif;font-size:14px;line-height:20px}button,input,select{font:inherit}button{cursor:pointer;min-height:32px;padding:5px 12px;border:1px solid var(--colorNeutralStroke1);border-radius:var(--borderRadiusMedium);background:var(--colorNeutralBackground1);color:var(--colorNeutralForeground1);font-weight:600;transition:background var(--durationFast)}button:hover{background:var(--colorNeutralBackground3)}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--colorBrandForegroundLink);outline-offset:3px}.primary{background:var(--colorBrandBackground);color:var(--colorNeutralForegroundOnBrand);border-color:transparent}.primary:hover{background:var(--colorBrandBackgroundHover)}.primary:active{background:var(--colorBrandBackgroundPressed)}.topbar{display:flex;justify-content:space-between;align-items:center;padding:12px 24px;background:var(--colorNeutralBackground1);border-bottom:1px solid var(--colorNeutralStroke2);gap:16px}.brand{display:flex;align-items:center;gap:12px;font-weight:600}.mark{display:grid;grid-template-columns:repeat(2,7px);gap:3px}.mark i{width:7px;height:7px;background:var(--colorBrandBackground)}.top-note{font-size:12px;color:var(--colorNeutralForeground3)}.page{max-width:1100px;margin:auto;padding:24px}.heading{display:flex;justify-content:space-between;align-items:center;gap:16px;margin-bottom:16px}.eyebrow{font-size:12px;line-height:16px;color:var(--colorNeutralForeground3);margin-bottom:4px}h1{font-size:24px;line-height:32px;font-weight:600;margin:0 0 4px}h2{font-size:20px;line-height:28px;font-weight:600;margin:0 0 16px}h3{font-size:16px;line-height:22px;font-weight:600;margin:20px 0 8px}p{margin:0 0 12px}.muted{color:var(--colorNeutralForeground3)}.tabs{display:flex;gap:16px;border-bottom:1px solid var(--colorNeutralStroke2);margin-bottom:24px}.tab{position:relative;background:none;border:0;border-radius:0;font-weight:400;padding:12px 4px;color:var(--colorNeutralForeground2)}.tab[aria-selected=true]{font-weight:600;color:var(--colorBrandForegroundLink)}.tab[aria-selected=true]:after{content:'';position:absolute;bottom:0;left:4px;right:4px;height:3px;background:var(--colorBrandBackground);border-radius:3px}.layout{display:grid;grid-template-columns:minmax(0,1fr) 240px;gap:24px}.document,.aside,.people{background:var(--colorNeutralBackground1);border:1px solid var(--colorNeutralStroke2);border-radius:var(--borderRadiusXLarge);padding:24px}.document{max-width:720px}.docmeta{display:flex;gap:8px;align-items:center;color:var(--colorNeutralForeground3);font-size:12px;line-height:16px;margin-bottom:24px}.avatar{flex:none;display:grid;place-items:center;background:var(--colorBrandBackground2);color:var(--colorBrandForegroundLink);font-weight:600;width:32px;height:32px;border-radius:50%}.quote{padding:12px 16px;background:var(--colorNeutralBackground2);border-left:3px solid var(--colorBrandBackground);margin:16px 0}.checklist{list-style:none;padding:0;margin:12px 0}.checklist li{display:flex;gap:8px;margin:8px 0}.checklist input{accent-color:var(--colorBrandBackground);width:16px;height:16px;margin-top:2px}.aside{padding:20px;height:fit-content}.aside h2{font-size:16px;line-height:22px;margin-bottom:16px}.person{display:flex;gap:8px;align-items:center;margin-bottom:16px}.person p{margin:0}.small{font-size:12px;line-height:16px}.aside hr{border:0;border-top:1px solid var(--colorNeutralStroke2);margin:20px 0}.tag{display:inline-block;border:1px solid var(--colorNeutralStroke1);padding:2px 8px;border-radius:4px;font-size:12px;color:var(--colorNeutralForeground2)}.people-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;gap:12px}.people-head h2{margin:0}.people ul{list-style:none;padding:0;margin:0}.member{display:flex;align-items:center;gap:12px;padding:16px 0;border-top:1px solid var(--colorNeutralStroke2)}.member-info{flex:1;min-width:0;overflow-wrap:anywhere}.member-info p{margin:0}.member .tag{flex:none}.caption{margin:20px 0 0;color:var(--colorNeutralForeground3);font-size:12px;line-height:16px}.status{margin-top:16px;overflow-wrap:anywhere;color:var(--colorNeutralForeground2)}.status:empty{display:none}[hidden]{display:none!important}dialog{width:min(520px,calc(100vw - 32px));padding:24px;border:1px solid var(--colorNeutralStroke1);border-radius:8px;background:var(--colorNeutralBackground1);color:var(--colorNeutralForeground1);max-height:calc(100dvh - 32px);overflow:auto}dialog::backdrop{background:rgba(0,0,0,.4)}dialog h2{font-size:20px;line-height:28px;margin-bottom:12px}.field{margin:20px 0}.field label{display:block;font-weight:600;margin-bottom:8px}.required{color:#b10e1e}.field input,.field select{width:100%;min-height:36px;background:var(--colorNeutralBackground1);color:var(--colorNeutralForeground1);padding:7px 12px;border:1px solid var(--colorNeutralStroke1);border-bottom-color:var(--colorNeutralForeground3);border-radius:4px}.field input:focus{border-bottom:2px solid var(--colorBrandBackground);padding-bottom:6px}.hint{font-size:12px;line-height:16px;color:var(--colorNeutralForeground3);margin-top:8px}.error{font-size:12px;line-height:16px;color:#b10e1e;margin-top:8px}.field input[aria-invalid=true]{border-color:#b10e1e}.actions{display:flex;justify-content:flex-end;gap:8px;margin-top:24px}[data-theme=dark] .error,[data-theme=dark] .required{color:#ff99a4}footer{padding:0 24px 20px;text-align:center;font-size:12px;color:var(--colorNeutralForeground3)}@media(max-width:700px){.page{padding:16px}.topbar{padding:12px 16px}.top-note{display:none}.layout{grid-template-columns:1fr}.document,.people{padding:20px}.aside{display:grid;grid-template-columns:1fr 1fr;gap:8px 16px}.aside h2,.aside hr{grid-column:1/-1}.aside hr{margin:0}.heading{align-items:flex-start}.heading .primary{flex:none}.member{flex-wrap:wrap}.member .tag{margin-left:44px}.topbar button,.heading button{min-height:40px}}@media(max-width:420px){h1{font-size:20px;line-height:28px}.heading{flex-direction:column;gap:12px}.aside{display:block}.member .tag{margin-left:0}}@media(prefers-reduced-motion:reduce){*{transition:none!important}}
</style></head><body>
<header class="topbar"><div class="brand"><span class="mark" aria-hidden="true"><i></i><i></i><i></i><i></i></span>Northstar workspace <span class="top-note">/ Product team</span></div><button id="theme" aria-pressed="false">Dark theme</button></header>
<main class="page"><div class="heading"><div><div class="eyebrow">Team workspace · October launch</div><h1>Launch workspace</h1><p class="muted">One brief. A clear decision. The right people.</p></div><button class="primary" id="share">Add a person</button></div>
<div class="tabs" role="tablist" aria-label="Workspace views"><button class="tab" id="brief-tab" role="tab" aria-controls="brief-panel" aria-selected="true" tabindex="0">Brief</button><button class="tab" id="people-tab" role="tab" aria-controls="people-panel" aria-selected="false" tabindex="-1">People <span id="count">2</span></button></div>
<section id="brief-panel" role="tabpanel" aria-labelledby="brief-tab" tabindex="0"><div class="layout"><article class="document"><div class="docmeta"><span class="avatar" aria-hidden="true">AC</span><span>Alex Chen · Product design<br>Updated today at 09:30</span></div><h2>A more welcoming first project</h2><p>Help a new team move from an empty workspace to its first useful result. This review focuses on the welcome screen and the first-project setup.</p><h3>The decision</h3><div class="quote">Should a sample project be created automatically, or offered as an option?</div><p>We want a clear starting point without filling the workspace with content someone did not choose. Review both paths before the team walkthrough.</p><h3>Review checklist</h3><ul class="checklist"><li><input id="c1" type="checkbox" checked><label for="c1">Welcome copy explains the next step</label></li><li><input id="c2" type="checkbox"><label for="c2">Empty workspace offers a sample project</label></li><li><input id="c3" type="checkbox"><label for="c3">Keyboard path reaches the first project action</label></li></ul><p class="small muted">Checklist changes are local to this example.</p></article><aside class="aside"><h2>Working together</h2><div class="person"><span class="avatar" aria-hidden="true">AC</span><div><p>Alex Chen</p><span class="small muted">Workspace owner</span></div></div><div class="person"><span class="avatar" aria-hidden="true">MP</span><div><p>Maya Patel</p><span class="small muted">Can edit</span></div></div><hr><div><h3>Next review</h3><p>Thursday, 10:00<br><span class="muted small">Team walkthrough · UTC</span></p></div><div><h3>Access</h3><span class="tag">Specific people</span><p class="small muted" style="margin-top:8px">Adding a person here updates only this local preview.</p></div></aside></div></section>
<section id="people-panel" role="tabpanel" aria-labelledby="people-tab" tabindex="0" hidden><div class="people"><div class="people-head"><h2>People with access</h2><span class="tag">Local example</span></div><p class="muted">The roles below are fictional. No invitation or real access change is made.</p><ul id="members"><li class="member"><span class="avatar" aria-hidden="true">AC</span><div class="member-info"><p>Alex Chen</p><span class="small muted">[email protected]</span></div><span class="tag">Owner</span></li><li class="member"><span class="avatar" aria-hidden="true">MP</span><div class="member-info"><p>Maya Patel</p><span class="small muted">[email protected]</span></div><span class="tag">Can edit</span></li></ul></div></section><p class="status" id="status" role="status"></p><p class="caption">Fluent UI React v9 web-token study · HTML/CSS interpretation, not native library components. Try tabs, theme, checkboxes and the access form.</p></main>
<dialog id="access" aria-labelledby="dialog-title"><form id="access-form" novalidate><h2 id="dialog-title">Add a person to Launch workspace</h2><p class="muted">Choose an address and permission. This demonstration updates the local list only.</p><div class="field"><label for="email">Email address <span class="required" aria-hidden="true">*</span></label><input id="email" type="email" autocomplete="email" aria-required="true" aria-describedby="email-hint" autofocus><div class="hint" id="email-hint">For example, [email protected]</div><div class="error" id="error" role="alert" hidden></div></div><div class="field"><label for="permission">Permission</label><select id="permission" aria-describedby="permission-hint"><option>Can view</option><option>Can edit</option></select><div class="hint" id="permission-hint">Viewers can read; editors can change the brief in a real application</div></div><div class="actions"><button type="button" id="cancel">Cancel</button><button class="primary" type="submit">Add locally</button></div></form></dialog>
<script>
const $=id=>document.getElementById(id),tabs=[...document.querySelectorAll('[role=tab]')],emails=new Set(['[email protected]','[email protected]']);
function selectTab(tab){tabs.forEach(t=>{const chosen=t===tab;t.setAttribute('aria-selected',String(chosen));t.tabIndex=chosen?0:-1;$(t.getAttribute('aria-controls')).hidden=!chosen;});}
tabs.forEach((tab,index)=>{tab.addEventListener('click',()=>selectTab(tab));tab.addEventListener('keydown',event=>{let next;if(event.key==='ArrowRight')next=(index+1)%tabs.length;if(event.key==='ArrowLeft')next=(index+tabs.length-1)%tabs.length;if(event.key==='Home')next=0;if(event.key==='End')next=tabs.length-1;if(next!==undefined){event.preventDefault();tabs.forEach(t=>t.tabIndex=-1);tabs[next].tabIndex=0;tabs[next].focus();}});});
$('theme').onclick=()=>{const dark=document.documentElement.dataset.theme!=='dark';document.documentElement.dataset.theme=dark?'dark':'light';$('theme').textContent=dark?'Light theme':'Dark theme';$('theme').setAttribute('aria-pressed',String(dark));};
const dialog=$('access'),email=$('email'),error=$('error');function clearError(){error.hidden=true;error.textContent='';email.removeAttribute('aria-invalid');email.setAttribute('aria-describedby','email-hint');}function showError(message){error.textContent=message;error.hidden=false;email.setAttribute('aria-invalid','true');email.setAttribute('aria-describedby','email-hint error');email.focus();}
$('share').onclick=()=>{clearError();dialog.showModal();email.focus();};$('cancel').onclick=()=>dialog.close();dialog.addEventListener('close',()=>$('share').focus());dialog.addEventListener('click',event=>{if(event.target===dialog){const r=dialog.getBoundingClientRect();if(event.clientX<r.left||event.clientX>r.right||event.clientY<r.top||event.clientY>r.bottom)dialog.close();}});email.addEventListener('input',clearError);
$('access-form').addEventListener('submit',event=>{event.preventDefault();const value=email.value.trim().toLowerCase();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)){showError('Enter an email address such as [email protected]');return;}if(emails.has(value)){showError('This address already appears in the local access list');return;}const permission=$('permission').value;emails.add(value);const item=document.createElement('li');item.className='member';const avatar=document.createElement('span');avatar.className='avatar';avatar.setAttribute('aria-hidden','true');avatar.textContent=value.slice(0,2).toUpperCase();const info=document.createElement('div');info.className='member-info';const name=document.createElement('p');name.textContent=value;const sub=document.createElement('span');sub.className='small muted';sub.textContent='Added in this local example';info.append(name,sub);const role=document.createElement('span');role.className='tag';role.textContent=permission;item.append(avatar,info,role);$('members').append(item);$('count').textContent=emails.size;$('status').textContent='Added '+value+' as '+permission.toLowerCase()+' locally. No invitation was sent.';email.value='';clearError();selectTab($('people-tab'));dialog.close();});
</script></body></html>
Bu belgedeki erişim iletişim kutusu ve sekmeler yerel HTML denetimlerini kullanır; FluentProvider, Field ve Dialog davranışını değerlendirmek için ayrı React v9 giriş noktasını çalıştırın.
Tam yerel uygulama girişi
Kurulumdan itibaren tam uygulama; odaklanmış API açıklamalarının yanında burada bir araya getirilmiştir. Kurulum bölümünde belgelenen paket ve derleme ön koşullarını izleyin.
jsx
// Terminal: npm install @fluentui/react-components@9
// Existing React application; index.html must contain <div id="root"></div>.
// Entry file: src/main.jsx
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import {
FluentProvider, webLightTheme, webDarkTheme, makeStyles, tokens,
Button, Body1, Title3, TabList, Tab, Field, Input, Select,
Dialog, DialogTrigger, DialogSurface, DialogBody, DialogTitle,
DialogContent, DialogActions
} from '@fluentui/react-components';
const useStyles = makeStyles({
page: { maxWidth: '880px', margin: '0 auto', padding: tokens.spacingHorizontalXXL },
header: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: tokens.spacingHorizontalL },
panel: { padding: tokens.spacingHorizontalXXL, marginTop: tokens.spacingVerticalL,
backgroundColor: tokens.colorNeutralBackground2, borderRadius: tokens.borderRadiusXLarge },
form: { display: 'grid', gap: tokens.spacingVerticalL },
row: { display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: tokens.spacingHorizontalM,
paddingTop: tokens.spacingVerticalM, paddingBottom: tokens.spacingVerticalM },
});
function Workspace({ dark, setDark }) {
const styles = useStyles();
const [tab, setTab] = React.useState('brief');
const [open, setOpen] = React.useState(false);
const [email, setEmail] = React.useState('');
const [permission, setPermission] = React.useState('Can view');
const [error, setError] = React.useState('');
const [notice, setNotice] = React.useState('');
const [people, setPeople] = React.useState([{ email: '[email protected]', permission: 'Owner' }]);
function submit(event) {
event.preventDefault();
const value = email.trim().toLowerCase();
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
setError('Enter an email address such as [email protected]'); return;
}
if (people.some(person => person.email === value)) {
setError('This address already appears in the local access list'); return;
}
setPeople(previous => [...previous, { email: value, permission }]);
setNotice(`Added ${value} as ${permission.toLowerCase()} in this local example. No invitation was sent.`);
setEmail(''); setError(''); setOpen(false); setTab('people');
}
return <main className={styles.page}>
<div className={styles.header}>
<Title3 as="h1">Launch workspace</Title3>
<Button onClick={() => setDark(!dark)}>{dark ? 'Light theme' : 'Dark theme'}</Button>
</div>
<Body1>Project brief and access settings · local demonstration</Body1>
<TabList aria-label="Workspace views" selectedValue={tab}
onTabSelect={(_, data) => setTab(data.value)}>
<Tab id="brief-tab" value="brief" aria-controls="brief-panel">Brief</Tab>
<Tab id="people-tab" value="people" aria-controls="people-panel">People</Tab>
</TabList>
<section id="brief-panel" role="tabpanel" aria-labelledby="brief-tab"
hidden={tab !== 'brief'} className={styles.panel} tabIndex={0}>
<h2>Launch a clearer onboarding flow</h2>
<p>Review the welcome screen and first-project setup before the team walkthrough.</p>
<p>Decision needed: should the sample project be created automatically or offered as an option?</p>
</section>
<section id="people-panel" role="tabpanel" aria-labelledby="people-tab"
hidden={tab !== 'people'} className={styles.panel} tabIndex={0}>
<h2>People with access</h2>
<ul>{people.map(person => <li key={person.email} className={styles.row}>
<span>{person.email}</span><strong>{person.permission}</strong>
</li>)}</ul>
</section>
<Dialog open={open} onOpenChange={(_, data) => setOpen(data.open)}>
<DialogTrigger disableButtonEnhancement><Button appearance="primary">Add a person</Button></DialogTrigger>
<DialogSurface>
<form noValidate onSubmit={submit}>
<DialogBody>
<DialogTitle>Add a person to Launch workspace</DialogTitle>
<DialogContent className={styles.form}>
<p>This example updates local state only. It does not send email or change real access.</p>
<Field label="Email address" required validationState={error ? 'error' : 'none'}
validationMessage={error || undefined}>
<Input type="email" value={email} onChange={(_, data) => { setEmail(data.value); setError(''); }} />
</Field>
<Field label="Permission" hint="Viewers can read; editors can change the brief in a real application">
<Select value={permission} onChange={(_, data) => setPermission(data.value)}>
<option>Can view</option><option>Can edit</option>
</Select>
</Field>
</DialogContent>
<DialogActions>
<DialogTrigger disableButtonEnhancement><Button type="button">Cancel</Button></DialogTrigger>
<Button type="submit" appearance="primary">Add locally</Button>
</DialogActions>
</DialogBody>
</form>
</DialogSurface>
</Dialog>
<p role="status">{notice}</p>
</main>;
}
function App() {
const [dark, setDark] = React.useState(false);
return <FluentProvider theme={dark ? webDarkTheme : webLightTheme}>
<Workspace dark={dark} setDark={setDark} />
</FluentProvider>;
}
createRoot(document.getElementById('root')).render(<App />);
Mevcut bir React uygulaması için eksiksiz React v9 bileşen bileşimi. Bağımsız önizleme, ayrı bir HTML/CSS yorumlamasıdır. Bu kod yalnızca yerel doğrulama ve durum güncellemeleri yapar; e-posta gönderilmez ve gerçek izin değişikliği gerçekleşmez. Kaynak API'leri incelenmiştir; hedef projede test edilmedikçe yerel paket derlemesinin çalıştığı iddia edilmez.
Field + Input + Select: etiketli tek bir erişim formu
Bu kod parçaları, aşağıdaki eksiksiz kurulumdaki durum değişkenlerini ve içe aktarımları kullanır. Etiket, açıklama ve doğrulama sunumundan Field sorumludur; e-posta kuralı ve yinelenen adres denetimi uygulamaya aittir.
required, erişilebilir bir zorunlu alan belirtimi sağlar; uygulama doğrulama kuralını uygulamaz.
validationMessage mevcutsa varsayılan doğrulama durumu error olur. Hata gösterilmemesi gereken durumlarda none değerini açıkça ayarlayın.
Input onChange, geçerli metni data.value içinde sağlar; işleyicisini yalnızca DOM olayını alan bir giriş öğesi işleyicisiyle karıştırmayın.
Panelleri açıkça ilişkilendirilmiş, denetimli TabList
TabList seçimi ve sekme davranışını denetler. İçerik panellerini kendiniz oluşturun ve kimliklerini ve erişilebilir adlarını her Tab ile ilişkilendirin.
selectedValue, TabList üzerinde bulunur; her Tab için farklı bir value kullanılır.
selectTabOnFocus varsayılan olarak false değerindedir. Bu davranış bilerek değiştirilmediği sürece ok tuşlarıyla odaklanma ve etkinleştirme birbirinden ayrıdır.
Fluent yatay sekmeler otomatik olarak yeni satıra geçmez veya kaydırılmaz; örnekte görünür kalmaları için iki kısa etiket kullanılır.
04
Microsoft Fluent 2 Tasarım Sistemi Yapay zekâ aracısı kurulumu
Uygulamaya Başlangıç Noktası
Mevcut bir React uygulaması için eksiksiz React v9 bileşen bileşimi. Bağımsız önizleme, ayrı bir HTML/CSS yorumlamasıdır. Bu kod yalnızca yerel doğrulama ve durum güncellemeleri yapar; e-posta gönderilmez ve gerçek izin değişikliği gerçekleşmez. Kaynak API'leri incelenmiştir; hedef projede test edilmedikçe yerel paket derlemesinin çalıştığı iddia edilmez.
jsx
// Terminal: npm install @fluentui/react-components@9
// Existing React application; index.html must contain <div id="root"></div>.
// Entry file: src/main.jsx
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import {
FluentProvider, webLightTheme, webDarkTheme, makeStyles, tokens,
Button, Body1, Title3, TabList, Tab, Field, Input, Select,
Dialog, DialogTrigger, DialogSurface, DialogBody, DialogTitle,
DialogContent, DialogActions
} from '@fluentui/react-components';
const useStyles = makeStyles({
page: { maxWidth: '880px', margin: '0 auto', padding: tokens.spacingHorizontalXXL },
header: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: tokens.spacingHorizontalL },
panel: { padding: tokens.spacingHorizontalXXL, marginTop: tokens.spacingVerticalL,
backgroundColor: tokens.colorNeutralBackground2, borderRadius: tokens.borderRadiusXLarge },
form: { display: 'grid', gap: tokens.spacingVerticalL },
row: { display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: tokens.spacingHorizontalM,
paddingTop: tokens.spacingVerticalM, paddingBottom: tokens.spacingVerticalM },
});
function Workspace({ dark, setDark }) {
const styles = useStyles();
const [tab, setTab] = React.useState('brief');
const [open, setOpen] = React.useState(false);
const [email, setEmail] = React.useState('');
const [permission, setPermission] = React.useState('Can view');
const [error, setError] = React.useState('');
const [notice, setNotice] = React.useState('');
const [people, setPeople] = React.useState([{ email: '[email protected]', permission: 'Owner' }]);
function submit(event) {
event.preventDefault();
const value = email.trim().toLowerCase();
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
setError('Enter an email address such as [email protected]'); return;
}
if (people.some(person => person.email === value)) {
setError('This address already appears in the local access list'); return;
}
setPeople(previous => [...previous, { email: value, permission }]);
setNotice(`Added ${value} as ${permission.toLowerCase()} in this local example. No invitation was sent.`);
setEmail(''); setError(''); setOpen(false); setTab('people');
}
return <main className={styles.page}>
<div className={styles.header}>
<Title3 as="h1">Launch workspace</Title3>
<Button onClick={() => setDark(!dark)}>{dark ? 'Light theme' : 'Dark theme'}</Button>
</div>
<Body1>Project brief and access settings · local demonstration</Body1>
<TabList aria-label="Workspace views" selectedValue={tab}
onTabSelect={(_, data) => setTab(data.value)}>
<Tab id="brief-tab" value="brief" aria-controls="brief-panel">Brief</Tab>
<Tab id="people-tab" value="people" aria-controls="people-panel">People</Tab>
</TabList>
<section id="brief-panel" role="tabpanel" aria-labelledby="brief-tab"
hidden={tab !== 'brief'} className={styles.panel} tabIndex={0}>
<h2>Launch a clearer onboarding flow</h2>
<p>Review the welcome screen and first-project setup before the team walkthrough.</p>
<p>Decision needed: should the sample project be created automatically or offered as an option?</p>
</section>
<section id="people-panel" role="tabpanel" aria-labelledby="people-tab"
hidden={tab !== 'people'} className={styles.panel} tabIndex={0}>
<h2>People with access</h2>
<ul>{people.map(person => <li key={person.email} className={styles.row}>
<span>{person.email}</span><strong>{person.permission}</strong>
</li>)}</ul>
</section>
<Dialog open={open} onOpenChange={(_, data) => setOpen(data.open)}>
<DialogTrigger disableButtonEnhancement><Button appearance="primary">Add a person</Button></DialogTrigger>
<DialogSurface>
<form noValidate onSubmit={submit}>
<DialogBody>
<DialogTitle>Add a person to Launch workspace</DialogTitle>
<DialogContent className={styles.form}>
<p>This example updates local state only. It does not send email or change real access.</p>
<Field label="Email address" required validationState={error ? 'error' : 'none'}
validationMessage={error || undefined}>
<Input type="email" value={email} onChange={(_, data) => { setEmail(data.value); setError(''); }} />
</Field>
<Field label="Permission" hint="Viewers can read; editors can change the brief in a real application">
<Select value={permission} onChange={(_, data) => setPermission(data.value)}>
<option>Can view</option><option>Can edit</option>
</Select>
</Field>
</DialogContent>
<DialogActions>
<DialogTrigger disableButtonEnhancement><Button type="button">Cancel</Button></DialogTrigger>
<Button type="submit" appearance="primary">Add locally</Button>
</DialogActions>
</DialogBody>
</form>
</DialogSurface>
</Dialog>
<p role="status">{notice}</p>
</main>;
}
function App() {
const [dark, setDark] = React.useState(false);
return <FluentProvider theme={dark ? webDarkTheme : webLightTheme}>
<Workspace dark={dark} setDark={setDark} />
</FluentProvider>;
}
createRoot(document.getElementById('root')).render(<App />);
Projeniz İçin Bir İstem
Ajan İstemi
@fluentui/react-components üzerinden Fluent UI React v9 kullanın. webLightTheme ile FluentProvider oluşturun ve webDarkTheme temasına geçiş seçeneği ekleyin. Kısa metni Title3 ve Body1 ile, denetimli TabList/Tab görünümlerini açık panellerle ve erişim Dialog'unu Field/Input/Select ile gerçek bir form kullanarak oluşturun. Özel yüzeyler ve boşluklar için belirteçlerden yararlanın. Subtitle1 için 20/28 değeri de dâhil olmak üzere sağlanan bileşen örneğini ve çalışma zamanı tür eşleşmelerini izleyin. Geçersiz girdiyi koruyun, yinelenen yerel adresleri reddedin, iletişim kutusu odağını geri yükleyin ve yerel eklemeleri gerçeğe uygun biçimde açıklayın. Paylaşım hizmetini ancak yetkilendirme ve sonuç sözleşmesi sağlandığında bağlayın.
05
Microsoft Fluent 2 Tasarım Sistemi Kaynaklar ve kapsam
Microsoft ürünü veya canlı paylaşım hizmeti olmayan, Fluent UI React v9 üzerine bir OpenDesign uygulama incelemesidir. Bağımsız önizleme, doğrulanmış web belirteçlerinin ve bileşen yapısının HTML/CSS yorumlamasıdır; aşağıdaki React kodu yerel Fluent bileşenlerini kullanır. Referans değerleri, 3 Ekim 2026 tarihinde incelenen bağlantılı kaynak anlık görüntüsünü yansıtır; her sürümde, platformda veya markada değerlerin aynı olacağına dair güvence vermez.