İpucu bileşeni oluşturma

Renk uyarlanabilir ve erişilebilir bir ipucu özel öğesi oluşturmaya yönelik temel bilgiler.

Bu yayında, renk 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 örneklerde 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üzleri için 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 üzerine gelindiğinde ya da odaklanıldığında gösterilir. Bir ipucu seçilemez veya doğrudan ipucuyla etkileşimde bulunulamaz. İpuçları, etiketlerin veya diğer yüksek değerli bilgilerin yerine geçmez. Kullanıcı, ipucu olmadan görevini tam olarak tamamlayabilmelidir.

Yapılması gereken: Girişlerinizi her zaman etiketleyin.
Etiket yerine ipuçları kullanmayın

İpucu ve İpucu Karşılaştırması

Birçok bileşen gibi ipucunun ne olduğuyla ilgili, örneğin MDN, WAI ARIA, Sarah Higley ve Kapsayıcı Bileşenler'de çeşitli açıklamalar vardır. İpuçları ile geçiş ipuçlarının birbirinden ayrılmasını seviyorum. İpucu, etkileşimli olmayan ek bilgiler içerirken bir açma/kapatma ipucu, etkileşim ve önemli bilgiler içerebilir. Farklılığın birincil nedeni erişilebilirliktir. Kullanıcıların pop-up'a nasıl gitmesi ve içindeki bilgi ile düğmelere nasıl erişebilmesi beklenmektedir. Açma/kapatma ipuçları kısa sürede karmaşık hale gelir.

Burada Designcember sitesindeki açma/kapatma ipucunu içeren bir videoyu görebilirsiniz. Kullanıcının sabitleyip keşfedebileceği, ardından ışığı kapatma veya Escape tuşu ile kapatabileceği etkileşimin yer aldığı bir yer paylaşımı:

Bu GUI Meydan Okuması, CSS ile hemen hemen her şeyi yapmayı amaçlayan bir ipucundan ibarettir. Şimdi, bunu nasıl oluşturacağınızı öğrenebilirsiniz.

Markup

<tool-tip> özel öğesi kullanmayı tercih ettim. Yazarların istemedikleri takdirde özel öğeler oluşturmasına gerek yoktur. Tarayıcı, <foo-bar> öğesini bir <div> gibi işler. Özel öğeyi daha az belirgin bir sınıf adı gibi düşünebilirsiniz. Bu süreçte JavaScript kullanılmaz.

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

Bu, içinde metin olan bir div gibidir. [role="tooltip"] özelliğini ekleyerek uygun ekran okuyucuların erişilebilirlik ağacını birbirine bağlayabiliriz.

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

Artık ekran okuyucularda bu özellik bir ipucu olarak görülüyor. Aşağıdaki örnekte, birinci bağlantı öğesinin ağacında tanınan bir ipucu öğesi varken ikinci öğenin nasıl olmadığı gösterilmiştir? İkincisi bu role sahip değildir. Stiller bölümünde bu ağaç görünümünü geliştireceğiz.

Chrome DevTools Erişilebilirlik Ağacı&#39;nın HTML&#39;yi temsil eden ekran görüntüsü. Odaklanabilir &#39;top ; tooltip var: Hey, bir ipucu!&#39; metnini içeren bir bağlantı gösterir. İçinde &quot;top&quot; şeklinde statik bir metin ve bir ipucu öğesi vardır.

Ardından, ipucunun odaklanılabilir olmaması gerekir. Ekran okuyucu, ipucu rolünü anlamazsa kullanıcıların içerikleri okumak için <tool-tip> öğesine odaklanmasını sağlar ve kullanıcı deneyimi buna ihtiyaç duymaz. Ekran okuyucular, içeriği üst öğeye ekler ve dolayısıyla içeriğin erişilebilir hale getirilmesi gerekmez. Burada, hiçbir kullanıcının sekme akışında bu ipucu içeriğini yanlışlıkla bulamayacağından emin olmak için inert kullanabiliriz:

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

Chrome DevTools Erişilebilirlik Ağacı&#39;nın başka bir ekran görüntüsü, bu sefer ipucu öğesi eksik.

Daha sonra, ipucunun konumunu belirtmek için arayüz olarak özellikleri kullanmayı seçtim. Varsayılan olarak tüm <tool-tip>'ler bir "en üst" konumu üstlenir, ancak konum tip-position eklenerek bir öğe üzerinde özelleştirilebilir:

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

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

Bunun gibi şeyler için sınıf yerine özellikleri kullanma eğilimindeyim. Böylece <tool-tip>, aynı anda ona birden fazla konum atanamaz. Yalnızca bir tane olabilir veya hiç olmayabilir.

Son olarak, ipucu sağlamak istediğiniz öğenin içine <tool-tip> öğeleri yerleştirin. Burada alt metnini, bir <picture> öğesinin içine resim ve bir <tool-tip> yerleştirerek 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;GUI Meydan Okuması kafatası logosu&quot; yazan bir ipucu içeren resmin ekran görüntüsü.

Burada, bir <abbr> öğesinin içine <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>

HTML kısaltmasının altı çizili olduğu ve üzerinde &quot;Köprü Metni Biçimlendirme Dili&quot; ipucunun bulunduğu bir paragrafın ekran görüntüsü.

Erişilebilirlik

Geçiş ipuçları değil de ipuçları oluşturmayı tercih ettiğim için bu bölüm çok daha basit. Öncelikle, beklediğimiz kullanıcı deneyimini ana hatlarıyla belirteyim:

  1. Kısıtlı alanlarda veya karmaşık arayüzlerde ek mesajları gizleyin.
  2. Kullanıcı bir öğenin üzerine geldiğinde, öğeyle etkileşim kurmak için öğeye odaklandığında veya dokunmayı kullandığında mesajı açığa çıkarın.
  3. Fareyle, odaklama veya dokunma sona erdiğinde iletiyi tekrar gizleyin.
  4. Son olarak, kullanıcı daha az hareket seçeneğini belirtmişse hareketin azaltıldığından emin olun.

Amacımız isteğe bağlı ek mesajlaşma sağlamaktır. Gören bir fare veya klavye kullanıcısı, fareyle iletinin üzerine gelerek iletiyi gözleriyle okuyabilir. Görme güçlüğü çeken bir ekran okuyucu kullanıcısı, aracı üzerinden işiterek mesajı almaya odaklanarak mesajı görüntüleyebilir.

MacOS VoiceOver'ın 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 haksızlığı ele aldık. Geriye kalan tek şey test edip kullanıcı deneyiminin, ipucu mesajını kullanıcıya uygun şekilde gösterdiğini doğrulamaktır. Yapılan testler sonucunda, sesli mesajın hangi bölümünün araç ipucu olduğu net değil. Erişilebilirlik ağacında hata ayıklarken de görülebilir. "Üst" bağlantı metni, hiç tereddüt etmeden "Bak, araç ipuçları!" ile birlikte çalıştırılır. Ekran okuyucu metni bölmez veya ipucu içeriği olarak tanımlamaz.

Chrome Geliştirici Araçları Erişilebilirlik Ağacı&#39;nın, bağlantı metninin &quot;top Hey, a tooltip!&quot; ifadesini içeren ekran görüntüsü.

<tool-tip> öğesine yalnızca sözde bir ekran okuyucu öğesi eklediğinizde, görme engelli kullanıcılar için kendi istem metnimizi 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, artık bağlantı metninden sonra noktalı virgül bulunan ve "Araç ipucu içeriyor: " ipucunu içeren güncellenmiş erişilebilirlik ağacını görebilirsiniz.

Chrome Geliştirici Araçları Erişilebilirlik Ağacı&#39;nın, &quot;top ; has tooltip: Hey, a tooltip!&quot; ifadesini iyileştiren güncellenmiş ekran görüntüsü.

Artık, ekran okuyucu kullanıcıları bağlantıya odaklandığında "top" (üstte) yazıp kısa bir süre durdurduktan sonra "iç ipucu: bak, araç ipuçları var" ifadesini duyurur. Bu, ekran okuyucu kullanıcısına kullanıcı deneyimiyle ilgili birkaç iyi ipucu verir. Bu tereddüt, bağlantı metni ile ipucunu kolayca ayırıyor. Ayrıca, "ipucu var" duyurulduğunda, bir ekran okuyucu kullanıcısı daha önce duymuşsa bunu kolayca iptal edebilir. Ek mesajı daha önce gördüğünüz gibi, fareyle üzerine gelme ve öğelerin üzerinde beklemeyi hızlıca geri alma işlemlerini hatırlatırız. Bu bana kullanıcı deneyimi eşdeğeri gibi geldi.

Stiller

<tool-tip> öğesi, temsil ettiği ek mesajı temsil eden öğenin alt öğesi olacaktır. Bu yüzden, öncelikle yer paylaşımı efektiyle ilgili temel bilgilerle başlayalım. position absolute ile dokümanı doküman akışının dışına çıkarın:

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

Üst öğe yığın bağlam değilse ipucu, kendisini en yakın olana konumlandırır. Bu, istemediğimiz bir şeydir. Blok üzerinde size yardımcı olabilecek yeni bir seçici var: :has():

Tarayıcı Desteği

  • 105
  • 105
  • 121
  • 15,4

Kaynak

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

Tarayıcı desteği konusunda çok endişelenmeyin. Öncelikle, bu ipuçlarının ek niteliğinde olduğunu unutmayın. Bunlar işe yaramazsa sorun yok demektir. İkinci olarak, JavaScript bölümünde :has() desteği olmayan tarayıcılarda ihtiyaç duyduğumuz işlevlerin çoklu doldurabilmesi için bir komut dosyası dağıtacağız.

Ardından, üst öğelerinden işaretçi etkinlikleri çalmamaları için ipuçlarını etkileşimli olmayacak şekilde yapalım:

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

Ardından, ipucunu çapraz geçişle geçirebilmemiz için ipucunu opaklıkla gizleyin:

tool-tip {
  opacity: 0;
}

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

Burada işin zor kısmını :is() ve :has() yapıyor. Bu nedenle, üst öğeler içeren tool-tip, alt ipucunun görünürlüğünü açıp kapatabileceği konusunda kullanıcı etkileşiminin farkındadır. Fare kullanıcıları fareyle üzerine gelip klavye ve ekran okuyucu kullanıcıları odaklanabilir ve kullanıcılar buna dokunabilir.

Gören kullanıcılar için "göster ve gizle" yer paylaşımıyla birlikte, üçgen şekli oluşturmak, konumlandırmak ve balona eklemek için bazı stiller eklemenin zamanı geldi. Aşağıdaki stiller, özel özellikleri kullanmaya başlar ve çok uzağa geldiğimiz noktalara eklemeler yapar. Ayrıca, kayan bir ipucu gibi görünmesi için gölgeler, yazı tipi ve renkler ekler:

Koyu moddaki ipucunun &quot;block-start&quot; bağlantısının üzerinde kayan 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 düzenlemeleri

Metin rengi sayfadan CanvasText sistem anahtar kelimesi aracılığıyla devralındığından ipucunda yalnızca birkaç renk yönetilir. Ayrıca, değerleri depolamak için özel özellikler oluşturduğumuzdan yalnızca bu özel özellikleri güncelleyebilir ve gerisini temaya bırakabiliriz:

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

İpucunun açık ve koyu versiyonlarının yan yana ekran görüntüsü.

Açık tema için arka planı beyaza uyarlıyoruz ve opaklıklarını ayarlayarak gölgeleri çok daha az güçlü hale getiriyoruz.

Sağdan sola

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

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

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

Bu, ipucunun konumlandırılmasına yardımcı olmak için kullanılabilir:

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

Ayrıca üçgenin nerede olduğuna dair yardım:

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() üzerinde mantıksal dönüşümler 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 kod, dört konumun her birinin hem soldan sağa hem de sağdan sola yönler için nasıl biçimlendirildiğini gösterir.

Üst 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 hizalama

Soldan sağa sağ konum ile sağdan sola satır içi uç konumu 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);
}

Alt ve blok uç hizalaması

Soldan sağa alt konum ile sağdan sola blok uç 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 sol 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);
}

Animasyonlar

Şu ana kadar yalnızca ipucunun görünürlüğünü değiştirdik. Bu bölümde, genel olarak güvenli bir azaltılmış hareket geçişi olduğundan, ilk olarak tüm kullanıcılar için opaklığı animasyon haline getireceğiz. Ardından, ipucunun üst öğeden kaymış gibi görünmesi için dönüştürme konumuna animasyon uygularız.

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

Opaklığı ve dönüşümü geçirmek için ipucu öğesinin stilini şu şekilde değiştirin:

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

Kenarların her biri için ipucu görünebilir. Kullanıcı, hareket etmeyi kabul ediyorsa çeviriX özelliğini şuralardan biraz uzaklaşarak 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 "out" durumu ayarlanıyor.

JavaScript

Bana göre, JavaScript isteğe bağlı. Bunun nedeni, kullanıcı arayüzünüzde bir görevi tamamlamak için bu ipuçlarından hiçbirinin okunması gerekmemesidir. Bu nedenle, ipuçları tamamen başarısız olursa, sorun yoktur. Bu, ipuçlarını da kademeli olarak geliştirilmiş olarak değerlendirebileceğimiz anlamına gelir. Sonunda tüm tarayıcılar :has() özelliğini destekleyecek ve bu komut dosyası tamamen kaldırılabilir.

Çoklu dolgu komut dosyası iki şey yapar ve bunu yalnızca, tarayıcı :has() özelliğini desteklemiyorsa yapar. İlk olarak :has() desteğini kontrol edin:

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

Daha sonra, <tool-tip> öğelerinin üst öğelerini bulun ve üzerinde ç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'))
}

Daha sonra, :has() seçicisini tam olarak aynı davranış için 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)
}

Hepsi bu kadar. Artık :has() desteklenmiyorsa tüm tarayıcılar ipuçlarını memnuniyetle gösterir.

Sonuç

Nasıl yapıldığını öğrendiğinize göre siz ne yapardınız? 🙂; açma/kapatma ipuçlarını kolaylaştıran popup API'yi, Z-endeksi savaşlarını ortadan kaldıran üst katman ve öğeleri pencereye daha iyi konumlandırmaya yönelik anchor API'yi sabırsızlıkla bekliyorum. O zamana kadar size ipuçları vereceğim.

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

Bir demo oluşturun, bana tweet atın bağlantıları, aşağıdaki topluluk remiksleri bölümüne ekleyeceğim.

Topluluk remiksleri

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

Kaynaklar