İpucu bileşeni oluşturma

Renk uyarlamalı ve erişilebilir bir ipucu özel öğesi oluşturmaya dair temel bilgiler.

Bu yayında, renge uyarlanabilir ve erişilebilir bir <tool-tip> özel öğesinin nasıl oluşturulacağıyla ilgili düşüncelerimi paylaşmak istiyorum. Demoyu deneyin ve kaynağı görüntüleyin.

Çeşitli örnekler ve renk şemalarında çalışan bir ipucu gösterilmektedir

Videoyu tercih ediyorsanız bu yayının YouTube sürümünü burada bulabilirsiniz:

Genel Bakış

İpucu, kullanıcı arayüzleriyle ilgili ek bilgiler içeren, modal olmayan, engellemeyen, etkileşimli olmayan bir yer paylaşımıdır. Varsayılan olarak gizlidir ve ilişkili bir öğenin fareyle üzerine gelindiğinde veya odaklandığında gösterilir. İpuçları seçilemez veya doğrudan etkileşim kurulamaz. İpuçları, etiketlerin veya diğer yüksek değerli bilgilerin yerini almaz. Kullanıcılar, ipucu olmadan görevlerini eksiksiz şekilde tamamlayabilmelidir.

Yapılması gereken: Girişlerinizi her zaman etiketleyin.
Yapılmaması gerekenler: Etiketler yerine ipuçlarını kullanma

Açma/kapatma ucu ve ipucu

Birçok bileşen gibi, ipucu kavramının da farklı açıklamaları vardır. Örneğin, MDN, WAI ARIA, Sarah Higley ve Inclusive Components'de bu kavram farklı şekillerde açıklanır. İpucu ve açma/kapatma ipuçlarının ayrılması hoşuma gitti. İpucunda etkileşimli olmayan ek bilgiler yer almalıdır. Açma/kapatma ipucu ise etkileşim ve önemli bilgileri içerebilir. Bu bölünmenin birincil nedeni erişilebilirliktir. Kullanıcıların pop-up'ta gezinmesi ve içindeki bilgi ve düğmelere nasıl erişimleri beklenmektedir. Açma/kapatma ipuçlarının karmaşıklığı hızla artar.

Designcember sitesindeki açma/kapatma ipucunun videosunu burada görebilirsiniz. Kullanıcının sabitleyip keşfedebileceği, ardından ışık kapatma veya çıkış tuşu ile kapatabileceği etkileşim içeren bir yer paylaşımı:

Bir ipucuyla sonuçlanan bu GUI Görevi, CSS ile hemen hemen her şeyi yapmaya çalışıyordu. GUI'nin nasıl oluşturulacağı aşağıda açıklanmıştır.

Brüt kar

Özel bir öğe <tool-tip> kullanmayı tercih ettim. Yazarların, istemedikleri takdirde web bileşenlerine özel öğeler eklemesi gerekmez. Tarayıcı, <foo-bar> öğesini <div> gibi işler. Daha az spesifik bir sınıf adı gibi özel bir öğeyi düşünebilirsiniz. JavaScript kullanılmaz.

<tool-tip>A tooltip</tool-tip>

Bu, içinde metin bulunan bir div gibidir. [role="tooltip"] ekleyerek uygun ekran okuyucuların erişilebilirlik ağacına bağlanabiliriz.

<tool-tip role="tooltip">A tooltip</tool-tip>

Ekran okuyucular artık bu öğeyi ipucu olarak tanır. Aşağıdaki örnekte ilk bağlantı öğesinin ağacında bir ipucu öğesinin nasıl tanındığını, ikincisinin ağacında nasıl tanınmadığını görün? İkinci kullanıcıda bu rol yoktur. Stil bölümünde bu ağaç görünümünü geliştireceğiz.

HTML&#39;yi temsil eden Chrome Geliştirici Araçları Erişilebilirlik Ağacı&#39;nın ekran görüntüsü. &quot;üst ; İpucu: Hey, bir ipucu!&quot; metnini içeren ve odaklanabilir olan bir bağlantı gösterir. İçinde &quot;üst&quot; statik metni ve bir ipucu öğesi bulunur.

Ardından, ipucunun odaklanılabilir olmaması gerekir. Ekran okuyucu, ipucu rolünü anlamazsa kullanıcıların, içeriği okumak için <tool-tip> öğesine odaklanmasına izin verir ve kullanıcı deneyiminde buna gerek yoktur. Ekran okuyucular, içeriği üst öğeye ekler ve bu nedenle erişilebilir hale getirmek için odaklanılmasına gerek yoktur. Kullanıcıların sekme akışlarında bu ipucu içeriğini yanlışlıkla bulmamasını sağlamak için inert kullanabiliriz:

<tool-tip inert role="tooltip">A tooltip</tool-tip>

Chrome Geliştirici Araçları Erişilebilirlik Ağacı&#39;nın başka bir ekran görüntüsü. Bu sefer ipucu öğesi eksik.

Ardından, ipucu metninin konumunu belirtmek için arayüz olarak özellikleri kullanmayı seçtim. Varsayılan olarak tüm <tool-tip> öğeleri "üst" konumda olur ancak tip-position eklenerek öğelerin konumu özelleştirilebilir:

<tool-tip role="tooltip" tip-position="right ">A tooltip</tool-tip>

Sağ tarafında &quot;Bir ipucu&quot; yazan bir ipucu içeren bağlantının ekran görüntüsü.

Bu tür durumlarda, <tool-tip> öğesine aynı anda birden fazla konum atanmaması için sınıflar yerine özellikler kullanmayı tercih ederim. Yalnızca bir tane veya hiç olmayabilir.

Son olarak, ipucu sağlamak istediğiniz öğenin içine <tool-tip> öğeleri yerleştirin. Burada, bir <picture> öğesinin içine bir resim ve <tool-tip> yerleştirerek alt metnini gören kullanıcılarla paylaşıyorum:

<picture>
  <img alt="The GUI Challenges skull logo" width="100" src="...">
  <tool-tip role="tooltip" tip-position="bottom">
    The <b>GUI Challenges</b> skull logo
  </tool-tip>
</picture>

&quot;The GUI Challenges kafatası logosu&quot; yazan bir ipucu içeren bir resmin ekran görüntüsü.

Burada bir <abbr> öğesinin içine bir <tool-tip> yerleştiriyorum:

<p>
  The <abbr>HTML <tool-tip role="tooltip" tip-position="top">Hyper Text Markup Language</tool-tip></abbr> abbr element.
</p>

Altı çizili HTML bulunan bir paragrafın ve üst kısmında &quot;Köprü Metni Biçimlendirme Dili&quot; yazan bir ipucunun ekran görüntüsü.

Erişilebilirlik

Açma/kapatma ipuçlarını değil, ipuçları oluşturmayı tercih ettiğim için bu bölüm çok daha basit. Öncelikle, istediğimiz kullanıcı deneyimini özetleyeyim:

  1. Sınırlı alanlarda veya dağınık arayüzlerde ek mesajları gizleyin.
  2. Kullanıcı bir öğeyle etkileşime geçmek için fareyle üzerine geldiğinde, öğeye odaklandığında veya dokunma özelliğini kullandığında mesajı gösterin.
  3. Fareyle üzerine gelme, odaklanma veya dokunma sona erdiğinde mesajı tekrar gizleyin.
  4. Son olarak, kullanıcı azaltılmış hareket tercihi belirtmişse hareketlerin azaltıldığından emin olun.

Hedefimiz isteğe bağlı ek mesajlardır. Görme engeli olmayan fare veya klavye kullanıcıları, fareyle mesajın üzerine gelerek mesajı görebilir ve okuyabilir. Görme engelli bir ekran okuyucu kullanıcısı, mesajı göstermek için odaklanabilir ve aracıyla mesajı sesli olarak alabilir.

MacOS VoiceOver'un ipucu içeren bir bağlantıyı okuduğu ekran görüntüsü

Önceki bölümde erişilebilirlik ağacını, ipucu rolünü ve atlatmayı ele aldık. Geriye kalan şey, ipucu mesajını kullanıcıya uygun şekilde test edip kullanıcı deneyimini doğrulamaktır. Test sonucunda, sesli mesajın hangi kısmının ipucu olduğu anlaşılamadı. Erişilebilirlik ağacında da hata ayıklama sırasında görülebilir. "Üst" bağlantı metni, "Bakın, ipuçları" ile birlikte duraksamadan birlikte çalıştırılır. Ekran okuyucu metni bozmaz veya ipucu içeriği olarak tanımlamaz.

Bağlantı metninde &quot;üst Hey, a tooltip!&quot; yazan Chrome Geliştirici Araçları Erişilebilirlik Ağacı&#39;nın ekran görüntüsü.

<tool-tip> öğesine ekran okuyucunun yalnızca sözde bir öğesi eklediğinizde, görme engelli kullanıcılar için kendi istem metnimizi de ekleyebiliriz.

&::before {
  content: "; Has tooltip: ";
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
}

Aşağıda, bağlantı metninin ardından noktalı virgül ve "İpucu var:" istemi bulunan güncellenmiş erişilebilirlik ağacını görebilirsiniz.

Chrome Geliştirici Araçları Erişilebilirlik Ağacı&#39;nın güncellenmiş ekran görüntüsü. Bağlantı metninde &quot;üst ; İpucu var: Hey, bir ipucu!&quot; şeklinde daha iyi bir ifade kullanılmış.

Artık ekran okuyucu kullanıcısı bağlantıya odaklandığında "üst" dedikten sonra kısa bir duraklıyor ve "ipucu var: bak, ipuçlarını" duyuruyor. Bu, ekran okuyucu kullanıcısına birkaç güzel kullanıcı deneyimi ipucu verir. Duraklatma, bağlantı metni ile ipucu arasında güzel bir ayrım sağlar. Ayrıca, "ipucu içeriyor" komutu duyurulduğunda, ekran okuyucu kullanıcısı bunu daha önce duymuşsa kolayca iptal edebilir. Ek mesajı gördüğünüz gibi, fareyle üzerine gelip hızlıca üzerine gelmeyi çok hatırlatır. Bu bize güzel bir kullanıcı deneyimi denkliği geldi.

Stiller

<tool-tip> öğesi, ek mesajı temsil ettiği öğenin alt öğesi olacaktır. Bu nedenle, önce yer paylaşımı efektinin temel özelliklerinden başlayalım. position absolute ile doküman akışından çıkarın:

tool-tip {
  position: absolute;
  z-index: 1;
}

Üst öğe, yığın bağlamı değilse ipucu kendisini en yakınına yerleştirir, yani istediğimiz bu değildir. Blokta size yardımcı olabilecek yeni bir seçici var: :has().

Tarayıcı desteği

  • Chrome: 105.
  • Kenar: 105.
  • Firefox: 121.
  • Safari: 15.4.

Kaynak

:has(> tool-tip) {
  position: relative;
}

Tarayıcı desteği konusunda çok fazla endişelenmeyin. Öncelikle, bu ipuçlarının tamamlayıcı olduğunu unutmayın. Bunlar çalışmazsa sorun olmayacaktır. İkinci olarak, JavaScript bölümünde :has() desteği olmayan tarayıcılar için ihtiyaç duyduğumuz işlevleri çoklu doldurmak üzere bir komut dosyası dağıtacağız.

Ardından, üst öğelerinden işaretçi etkinliklerini çalmamaları için ipuçlarının etkileşimli olmamasını sağlayalım:

tool-tip {
  
  pointer-events: none;
  user-select: none;
}

Ardından, ipucunu opaklıkla gizleyerek ipucunu çapraz geçişle geçirebiliriz:

tool-tip {
  opacity: 0;
}

:has(> tool-tip):is(:hover, :focus-visible, :active) > tool-tip {
  opacity: 1;
}

Buradaki işin zor kısmı :is() ve :has() tarafından üstlenilerek üst öğe içeren tool-tip, kullanıcı etkileşiminin farkında olmasını sağlayarak alt ipucunun görünürlüğünü açar/kapatır. Fare kullanıcıları fareyle üzerine gelebilir, klavye ve ekran okuyucu kullanıcıları odaklanabilir ve dokunabilir.

Görme engeli olmayan kullanıcılar için gösterme ve gizleme yer paylaşımı çalışmaktadır. Şimdi tema oluşturma, konumlandırma ve balona üçgen şekli ekleme için bazı stiller ekleme zamanı. Aşağıdaki stillerde, özel mülkler kullanılmaya başlanır. Bu stiller, şimdiye kadarki deneyimlerimizi temel alır ancak yüzen bir ipucu gibi görünmesi için gölgeler, yazı tipleri ve renkler de ekler:

&quot;block-start&quot; bağlantısının üzerinde duran, koyu moddaki ipucum ekran görüntüsü.

tool-tip {
  --_p-inline: 1.5ch;
  --_p-block: .75ch;
  --_triangle-size: 7px;
  --_bg: hsl(0 0% 20%);
  --_shadow-alpha: 50%;

  --_bottom-tip: conic-gradient(from -30deg at bottom, rgba(0,0,0,0), #000 1deg 60deg, rgba(0,0,0,0) 61deg) bottom / 100% 50% no-repeat;
  --_top-tip: conic-gradient(from 150deg at top, rgba(0,0,0,0), #000 1deg 60deg, rgba(0,0,0,0) 61deg) top / 100% 50% no-repeat;
  --_right-tip: conic-gradient(from -120deg at right, rgba(0,0,0,0), #000 1deg 60deg, rgba(0,0,0,0) 61deg) right / 50% 100% no-repeat;
  --_left-tip: conic-gradient(from 60deg at left, rgba(0,0,0,0), #000 1deg 60deg, rgba(0,0,0,0) 61deg) left / 50% 100% no-repeat;

  pointer-events: none;
  user-select: none;

  opacity: 0;
  transform: translateX(var(--_x, 0)) translateY(var(--_y, 0));
  transition: opacity .2s ease, transform .2s ease;

  position: absolute;
  z-index: 1;
  inline-size: max-content;
  max-inline-size: 25ch;
  text-align: start;
  font-size: 1rem;
  font-weight: normal;
  line-height: normal;
  line-height: initial;
  padding: var(--_p-block) var(--_p-inline);
  margin: 0;
  border-radius: 5px;
  background: var(--_bg);
  color: CanvasText;
  will-change: filter;
  filter:
    drop-shadow(0 3px 3px hsl(0 0% 0% / var(--_shadow-alpha)))
    drop-shadow(0 12px 12px hsl(0 0% 0% / var(--_shadow-alpha)));
}

/* create a stacking context for elements with > tool-tips */
:has(> tool-tip) {
  position: relative;
}

/* when those parent elements have focus, hover, etc */
:has(> tool-tip):is(:hover, :focus-visible, :active) > tool-tip {
  opacity: 1;
  transition-delay: 200ms;
}

/* prepend some prose for screen readers only */
tool-tip::before {
  content: "; Has tooltip: ";
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
}

/* tooltip shape is a pseudo element so we can cast a shadow */
tool-tip::after {
  content: "";
  background: var(--_bg);
  position: absolute;
  z-index: -1;
  inset: 0;
  mask: var(--_tip);
}

/* top tooltip styles */
tool-tip:is(
  [tip-position="top"],
  [tip-position="block-start"],
  :not([tip-position]),
  [tip-position="bottom"],
  [tip-position="block-end"]
) {
  text-align: center;
}

Tema ayarları

Metin rengi, CanvasText sistem anahtar kelimesi aracılığıyla sayfadan devralındığından ipucu metninde yönetilecek yalnızca birkaç renk vardır. Ayrıca, değerleri depolamak için özel özellikler oluşturduğumuzdan yalnızca bu özel özellikleri güncelleyebilir ve geri kalanını temanın halletmesine izin verebiliriz:

@media (prefers-color-scheme: light) {
  tool-tip {
    --_bg: white;
    --_shadow-alpha: 15%;
  }
}

İpucu metninin açık ve koyu tema sürümlerinin yan yana ekran görüntüsü.

Açık tema için, arka planı beyaza ayarlar ve opaklıklarını ayarlayarak gölgeleri çok daha güçlü hale getiririz.

Sağdan sola

Sağdan sola okuma modlarını desteklemek için özel bir özellik, belge yönünün değerini sırasıyla -1 veya 1 değerinde saklar.

tool-tip {
  --isRTL: -1;
}

tool-tip:dir(rtl) {
  --isRTL: 1;
}

Bu, ipucunu konumlandırmaya yardımcı olmak için kullanılabilir:

tool-tip[tip-position="top"]) {
  --_x: calc(50% * var(--isRTL));
}

Üçgenin nerede olduğuna yardımcı olmanın yanı sıra:

tool-tip[tip-position="right"]::after {
  --_tip: var(--_left-tip);
}

tool-tip[tip-position="right"]:dir(rtl)::after {
  --_tip: var(--_right-tip);
}

Son olarak, translateX() üzerindeki mantıksal dönüştürmeler için de kullanılabilir:

--_x: calc(var(--isRTL) * -3px * -1);

İpucu konumlandırma

Hem fiziksel hem de mantıksal ipucu konumlarını işlemek için ipucunu inset-block veya inset-inline özellikleriyle mantıksal olarak konumlandırın. Aşağıdaki kodda, dört konumun her birinin hem soldan sağa hem de sağdan sola yönler için nasıl biçimlendirildiği gösterilmektedir.

Üste ve blok başlangıç hizalaması

Soldan sağa üst konum ile sağdan sola üst konum arasındaki yerleşim farkını gösteren ekran görüntüsü.

tool-tip:is([tip-position="top"], [tip-position="block-start"], :not([tip-position])) {
  inset-inline-start: 50%;
  inset-block-end: calc(100% + var(--_p-block) + var(--_triangle-size));
  --_x: calc(50% * var(--isRTL));
}

tool-tip:is([tip-position="top"], [tip-position="block-start"], :not([tip-position]))::after {
  --_tip: var(--_bottom-tip);
  inset-block-end: calc(var(--_triangle-size) * -1);
  border-block-end: var(--_triangle-size) solid transparent;
}

Sağa ve satır içi uç hizalama

Soldan sağa doğru sağ konum ile sağdan sola doğru satır içi son konum arasındaki yerleşim farkını gösteren ekran görüntüsü.

tool-tip:is([tip-position="right"], [tip-position="inline-end"]) {
  inset-inline-start: calc(100% + var(--_p-inline) + var(--_triangle-size));
  inset-block-end: 50%;
  --_y: 50%;
}

tool-tip:is([tip-position="right"], [tip-position="inline-end"])::after {
  --_tip: var(--_left-tip);
  inset-inline-start: calc(var(--_triangle-size) * -1);
  border-inline-start: var(--_triangle-size) solid transparent;
}

tool-tip:is([tip-position="right"], [tip-position="inline-end"]):dir(rtl)::after {
  --_tip: var(--_right-tip);
}

Alta ve blok ucuna hizalama

Soldan sağa alt konum ile sağdan sola blok sonu konumu arasındaki yerleşim farkını gösteren ekran görüntüsü.

tool-tip:is([tip-position="bottom"], [tip-position="block-end"]) {
  inset-inline-start: 50%;
  inset-block-start: calc(100% + var(--_p-block) + var(--_triangle-size));
  --_x: calc(50% * var(--isRTL));
}

tool-tip:is([tip-position="bottom"], [tip-position="block-end"])::after {
  --_tip: var(--_top-tip);
  inset-block-start: calc(var(--_triangle-size) * -1);
  border-block-start: var(--_triangle-size) solid transparent;
}

Sola ve satır içi başlangıç hizalaması

Soldan sağa soldaki konum ile sağdan sola satır içi başlangıç konumu arasındaki yerleşim farkını gösteren ekran görüntüsü.

tool-tip:is([tip-position="left"], [tip-position="inline-start"]) {
  inset-inline-end: calc(100% + var(--_p-inline) + var(--_triangle-size));
  inset-block-end: 50%;
  --_y: 50%;
}

tool-tip:is([tip-position="left"], [tip-position="inline-start"])::after {
  --_tip: var(--_right-tip);
  inset-inline-end: calc(var(--_triangle-size) * -1);
  border-inline-end: var(--_triangle-size) solid transparent;
}

tool-tip:is([tip-position="left"], [tip-position="inline-start"]):dir(rtl)::after {
  --_tip: var(--_left-tip);
}

Animasyon

Şu ana kadar yalnızca ipucunun görünürlüğünü değiştirdik. Bu bölümde, genellikle güvenli olan azaltılmış hareket geçişi olduğundan ilk olarak tüm kullanıcılar için opaklığı animasyonlu olarak değiştireceğiz. Ardından, ipucu üst öğeden kayıyormuş gibi görünmesi için dönüştürme konumunu animasyonlu hale getireceğiz.

Güvenli ve anlamlı bir varsayılan geçiş

İpucu öğesini, opaklık ve dönüşüm geçişi yapacak şekilde şu şekilde biçimlendirin:

tool-tip {
  opacity: 0;
  transform: translateX(var(--_x, 0)) translateY(var(--_y, 0));
  transition: opacity .2s ease, transform .2s ease;
}

:has(> tool-tip):is(:hover, :focus-visible, :active) > tool-tip {
  opacity: 1;
  transition-delay: 200ms;
}

Geçişe hareket ekleme

Kullanıcı hareketi kabul ediyorsa, ipucu gösterilebilecek her kenar için translateX mülküne hareket edeceği küçük bir mesafe vererek mülkü hafifçe konumlandırın:

@media (prefers-reduced-motion: no-preference) {
  :has(> tool-tip:is([tip-position="top"], [tip-position="block-start"], :not([tip-position]))):not(:hover):not(:focus-visible):not(:active) tool-tip {
    --_y: 3px;
  }

  :has(> tool-tip:is([tip-position="right"], [tip-position="inline-end"])):not(:hover):not(:focus-visible):not(:active) tool-tip {
    --_x: -3px;
  }

  :has(> tool-tip:is([tip-position="bottom"], [tip-position="block-end"])):not(:hover):not(:focus-visible):not(:active) tool-tip {
    --_y: -3px;
  }

  :has(> tool-tip:is([tip-position="left"], [tip-position="inline-start"])):not(:hover):not(:focus-visible):not(:active) tool-tip {
    --_x: 3px;
  }
}

"Giriş" durumu translateX(0) olduğundan bu ayarın "out" olarak belirlendiğine dikkat edin.

JavaScript

JavaScript'i isteğe bağlı olarak kullanabilirsiniz. Bunun nedeni, bu ipuçlarından hiçbirinin kullanıcı arayüzünüzde bir görevi tamamlamak için okunması gerekmemesidir. Bu nedenle, ipuçları tamamen başarısız olursa bu çok önemli bir sorun değildir. Bu, ipuçları için aşamalı olarak geliştirme yapabileceğimiz anlamına da gelir. Tüm tarayıcılar :has()'ü desteklemeye başladığında bu komut dosyası tamamen kaldırılabilir.

Polifill komut dosyası iki işlem yapar ve bunu yalnızca tarayıcı :has()'ü desteklemiyorsa yapar. İlk olarak :has() desteği olup olmadığını kontrol edin:

if (!CSS.supports('selector(:has(*))')) {
  // do work
}

Ardından, <tool-tip> öğelerinin üst öğelerini bulun ve bunlara çalışacakları bir sınıf adı verin:

if (!CSS.supports('selector(:has(*))')) {
  document.querySelectorAll('tool-tip').forEach(tooltip =>
    tooltip.parentNode.classList.add('has_tool-tip'))
}

Ardından, aynı davranış için :has() seçiciyi simüle ederek bu sınıf adını kullanan bir stil grubu ekleyin:

if (!CSS.supports('selector(:has(*))')) {
  document.querySelectorAll('tool-tip').forEach(tooltip =>
    tooltip.parentNode.classList.add('has_tool-tip'))

  let styles = document.createElement('style')
  styles.textContent = `
    .has_tool-tip {
      position: relative;
    }
    .has_tool-tip:is(:hover, :focus-visible, :active) > tool-tip {
      opacity: 1;
      transition-delay: 200ms;
    }
  `
  document.head.appendChild(styles)
}

Bu kadar. :has() desteklenmiyorsa artık tüm tarayıcılar ipuçları gösterecektir.

Sonuç

Bunu nasıl yaptığımı öğrendiniz. Siz nasıl yapardınız? 🙂 Açma/kapatma ipuçlarını kolaylaştırmak için popup API'sini, z-dizin savaşları yaşamamak için üst katmanı ve öğeleri pencerede daha iyi konumlandırmak için anchor API'sini kullanmayı sabırsızlıkla bekliyorum. O zamana kadar açıklama metinleri hazırlıyorum.

Yaklaşımlarımızı çeşitlendirelim ve web'de uygulama geliştirmenin tüm yollarını öğrenelim.

Demo oluşturup beni tweet'le bağlantıları oluşturduğumda bunu aşağıdaki topluluk remiksleri bölümüne ekleyeceğim.

Topluluk remiksleri

Henüz burada görülecek bir şey yok.

Kaynaklar