Çapraz platform ürünü · Yeni kaynak

Microsoft Fluent 2 Tasarım Sistemi.

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.

Proje özeti ve erişim iletişim kutusuÖrneğin Tamamını Aç
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.

Açık temanın nötr rolleri

Kaynak
Ad / KuralDeğer / YapılandırmaKullanım
colorNeutralForeground1#242424 Birincil belge metni.
colorNeutralForeground2#424242 İkincil etiketler.
colorNeutralForeground3#616161 Üst veriler; son karşıtlığı yine de kontrol edin.
colorNeutralBackground1#ffffff Ana yüzey.
colorNeutralBackground2#fafafa Destekleyici, daha sakin yüzey.
colorNeutralBackground3#f5f5f5 Çalışma alanı tuvali.
colorNeutralStroke1#d1d1d1 Standart bileşen kenarlığı.
colorNeutralStroke2#e0e0e0 İnce ayırıcı.
colorNeutralStrokeAccessible#616161 Daha yüksek karşıtlıklı denetim öğesi kenarlığı.
colorNeutralForegroundOnBrand#ffffff Desteklenen marka dolguları üzerindeki metin.
colorStrokeFocus1 / colorStrokeFocus2#ffffff / #000000İki odak rengi; bileşen kenarındaki karşıtlığı koruyun.

brandWeb'den çözümlenen web marka rolleri

Kaynak
Ad / KuralDeğer / YapılandırmaKullanım
colorBrandBackground#0f6cbd · brandWeb[80]webLightTheme içindeki birincil eylem.
colorBrandBackgroundHover#115ea3 · brandWeb[70]Bu eylemin üzerine gelme durumu.
colorBrandBackgroundPressed#0c3b5e · brandWeb[40]Basılı tutma durumu; genel bir seçili durumu değildir.
colorBrandBackground2#ebf3fc · brandWeb[160]Hafif marka arka planı.
colorBrandForegroundLink#115ea3 · brandWeb[70]Açık web temasındaki bağlantı ön plan rengi.

Yatay ve dikey aralık

Kaynak
Ad / KuralDeğer / YapılandırmaKullanım
spacingHorizontalNone / spacingVerticalNone0Düzen yönüne göre ekseni seçin; ortak aralık adımı.
spacingHorizontalXXS / spacingVerticalXXS2pxDüzen yönüne göre ekseni seçin; ortak aralık adımı.
spacingHorizontalXS / spacingVerticalXS4pxDüzen yönüne göre ekseni seçin; ortak aralık adımı.
spacingHorizontalSNudge / spacingVerticalSNudge6pxDüzen yönüne göre ekseni seçin; simge hizalama düzeltmesi.
spacingHorizontalS / spacingVerticalS8pxDüzen yönüne göre ekseni seçin; ortak aralık adımı.
spacingHorizontalMNudge / spacingVerticalMNudge10pxDüzen yönüne göre ekseni seçin; simge hizalama düzeltmesi.
spacingHorizontalM / spacingVerticalM12pxDüzen yönüne göre ekseni seçin; ortak aralık adımı.
spacingHorizontalL / spacingVerticalL16pxDüzen yönüne göre ekseni seçin; ortak aralık adımı.
spacingHorizontalXL / spacingVerticalXL20pxDüzen yönüne göre ekseni seçin; ortak aralık adımı.
spacingHorizontalXXL / spacingVerticalXXL24pxDüzen yönüne göre ekseni seçin; ortak aralık adımı.
spacingHorizontalXXXL / spacingVerticalXXXL32pxDüzen yönüne göre ekseni seçin; ortak aralık adımı.

React web tipografi bileşimleri

Kaynak
Ad / KuralDeğer / YapılandırmaKullanım
Caption2 · fontSizeBase10010px / 14px / 400Boyut / satır yüksekliği / kalınlık. lineHeightBase100 ile birlikte kullanın.
Caption1 · fontSizeBase20012px / 16px / 400Boyut / satır yüksekliği / ağırlık. lineHeightBase200 ile eşleştirin.
Body1 · fontSizeBase30014px / 20px / 400Boyut / satır yüksekliği / ağırlık. lineHeightBase300 ile eşleştirin.
Body2 / Subtitle2 · fontSizeBase40016px / 22px / 400 / 600Boyut / satır yüksekliği / ağırlık. lineHeightBase400 ile eşleştirin.
Subtitle1 · fontSizeBase50020px / 28px / 600Boyut / satır yüksekliği / ağırlık. lineHeightBase500 ile eşleştirin.
Title3 · fontSizeBase60024px / 32px / 600Boyut / satır yüksekliği / ağırlık. lineHeightBase600 ile eşleştirin.
Title2 · fontSizeHero70028px / 36px / 600Boyut / satır yüksekliği / ağırlık. lineHeightHero700 ile eşleştirin.
Title1 · fontSizeHero80032px / 40px / 600Boyut / satır yüksekliği / ağırlık. lineHeightHero800 ile eşleştirin.
LargeTitle · fontSizeHero90040px / 52px / 600Boyut / satır yüksekliği / ağırlık. lineHeightHero900 ile eşleştirin.
Display · fontSizeHero100068px / 92px / 600Boyut / satır yüksekliği / ağırlık. lineHeightHero1000 ile eşleştirin.

Yazı tipi ailesi ve ağırlığı

Kaynak
Ad / KuralDeğer / YapılandırmaKullanım
fontFamilyBase'Segoe UI', 'Segoe UI Web (West European)', -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serifSağlayıcının varsayılan web yazı tipi yığınıdır; çevredeki site yazı tipini Fluent olarak adlandırmayın.
fontFamilyMonospaceConsolas, 'Courier New', Courier, monospaceKod ve teknik tanımlayıcılar.
fontWeightRegular / Medium / Semibold / Bold400 / 500 / 600 / 700Rol ağırlığını, eşleştirilmiş yazı ölçeğiyle birlikte kullanın.

Şekil belirteçleri

Kaynak
Ad / KuralDeğer / YapılandırmaKullanım
borderRadiusNone0Önce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadiusSmall2pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadiusMedium4pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadiusLarge6pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadiusXLarge8pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadius2XLarge12pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadius3XLarge16pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadius4XLarge24pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadius5XLarge32pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadius6XLarge40pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.
borderRadiusCircular10000pxÖnce bileşenin varsayılanını kullanın; bu, kullanılabilir bir belirteçtir, her yüzey için zorunlu değildir.

Hareket süresi aralığı

Kaynak
Ad / KuralDeğer / YapılandırmaKullanım
durationUltraFast50msKullanılabilir bir çalışma zamanı belirtecidir; örnekte üzerine gelme için Fast kullanılır ve azaltılmış hareket tercihinde geçişler kaldırılır.
durationFaster100msKullanılabilir bir çalışma zamanı belirtecidir; örnekte üzerine gelme için Fast kullanılır ve azaltılmış hareket tercihinde geçişler kaldırılır.
durationFast150msKullanılabilir bir çalışma zamanı belirtecidir; örnekte üzerine gelme için Fast kullanılır ve azaltılmış hareket tercihinde geçişler kaldırılır.
durationNormal200msKullanılabilir bir çalışma zamanı belirtecidir; örnekte üzerine gelme için Fast kullanılır ve azaltılmış hareket tercihinde geçişler kaldırılır.
durationGentle250msKullanılabilir bir çalışma zamanı belirtecidir; örnekte üzerine gelme için Fast kullanılır ve azaltılmış hareket tercihinde geçişler kaldırılır.
durationSlow300msKullanılabilir bir çalışma zamanı belirtecidir; örnekte üzerine gelme için Fast kullanılır ve azaltılmış hareket tercihinde geçişler kaldırılır.
durationSlower400msKullanılabilir bir çalışma zamanı belirtecidir; örnekte üzerine gelme için Fast kullanılır ve azaltılmış hareket tercihinde geçişler kaldırılır.
durationUltraSlow500msKullanılabilir bir çalışma zamanı belirtecidir; örnekte üzerine gelme için Fast kullanılır ve azaltılmış hareket tercihinde geçişler kaldırılır.

Tema değişiklikleri tersine çevirme değil, anlamsal değişikliklerdir

Kaynak
Ad / KuralDeğer / YapılandırmaKullanım
colorNeutralBackground1#292929 Açık #ffffff yerine koyu ana yüzey.
colorNeutralForeground1#ffffff Açık #242424 yerine koyu birincil metin.
colorNeutralForeground2#d6d6d6 Koyu ikincil metin.
colorBrandBackgroundbrandWeb[70] = #115ea3Koyu birincil dolgu; açık modda brandWeb[80] kullanılır.
colorBrandForegroundLinkbrandWeb[100] = #479ef5Koyu bağlantı rolü; birincil düğme dolgusundan farklıdır.
03

Microsoft Fluent 2 Tasarım Sistemi: Eksiksiz Kılavuz

Bu sistemi gerçek bir arayüz için seçmeye, uygulamaya ve gözden geçirmeye yönelik pratik bir kılavuz.

DESIGN-microsoft-fluent.md

Eksiksiz yönlendirme, uygulama için başlangıç noktası ve kaynak bağlantıları.

Kılavuzu İndir

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.

Bu OpenDesign kılavuzundan

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.

Destekleyici belirtimi okuyun

Sağlayıcının gerçekte sundukları

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.

Destekleyici belirtimi okuyun

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.

Destekleyici belirtimi okuyun

Koyu mod eşlemeyi değiştirir

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.

Destekleyici belirtimi okuyun

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.

Destekleyici belirtimi okuyun

Metin hazır ayarları ve yazı tipi yığını

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.

Destekleyici belirtimi okuyun

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.

Destekleyici belirtimi okuyun

Şekil, bileşen ölçeğini tanımlar

Destekleyici belirtimi okuyun

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.

Destekleyici belirtimi okuyun

Kategori sayısı arttığında farklı bir desen seçin

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.

Destekleyici belirtimi okuyun

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.

Destekleyici belirtimi okuyun

Doğrulama durumunu izin durumundan ayrı tutun

İ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.

Destekleyici belirtimi okuyun

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.

Destekleyici belirtimi okuyun

Gerçek karara uygun modal davranışını seçin

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.

Destekleyici belirtimi okuyun

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.

Destekleyici belirtimi okuyun

Yorumlamanın sınırlarını sınayın

Destekleyici belirtimi okuyun

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.

  1. Mevcut React projesine @fluentui/react-components@9 yükleyin ve oluşturulan kilit dosyasını koruyun.
  2. Sağlanan giriş noktasını, başlangıç teması olarak webLightTheme kullanılan tek bir FluentProvider altında oluşturun.
  3. İ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.
  4. webDarkTheme ve klavye gezintisiyle de tekrarlayın; panel ilişkilendirmesini, hata duyurusunu ve odağın geri dönmesini doğrulayın.

Destekleyici belirtimi okuyun

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.

Destekleyici belirtimi okuyun

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>

Kod referansı

  • 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 />);

Kod referansı

  • 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.

jsx
<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="Choose what this person can do">
  <Select value={permission}
    onChange={(_, data) => setPermission(data.value)}>
    <option>Can view</option><option>Can edit</option>
  </Select>
</Field>

Kod referansı

  • 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.

jsx
<TabList selectedValue={tab} aria-label="Workspace views"
  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'} tabIndex={0}>Project brief</section>
<section id="people-panel" role="tabpanel" aria-labelledby="people-tab"
  hidden={tab !== 'people'} tabIndex={0}>People with access</section>

Kod referansı

  • 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.