---
title: 'Renk Sistemi'
source: 'https://academia.sh/tr/kurslar/arayuz-temelleri/renk-sistemi'
course: 'Arayüz Tasarımının Temelleri'
language: tr
updated: '2026-08-17T18:09:36+00:00'
license: 'CC BY-SA 4.0'
---

# Renk Sistemi

Rengin rol olarak tanımlanması, tek tondan açıklık ekseninde rampa üretimi, rollerin basamaklara bağlanması ve eşleşmelerin bağıl parlaklık üzerinden denetlenmesi.

Önceki üç ders metnin biçimini sabitledi. Renk tarafında ise elde yalnızca ilk dersten
kalan iki değer var: koyu gri metin ve beyaz zemin. Katalog arayüzü bununla yürümez.
Ödünç alma düğmesinin birincil eylem olduğu, seçili kaydın seçili olduğu, bir kaydın rafta
bulunmadığı — bunların hepsi ayrı yüzeyler ve ayrı metin renkleri ister.

Buradaki asıl risk renk seçmek değil, renkleri **tek tek** seçmektir. Her ihtiyaç
doğduğunda bir renk daha eklenirse arayüz kısa sürede birbirine yakın onlarca değer taşır;
hangisinin nerede kullanılacağı kimsenin bilmediği bir liste oluşur. Ölçek tipografide bu
sorunu nasıl çözdüyse, renk sistemi de aynı biçimde çözer: değerler tek tek seçilmez,
**türetilir**; ve kullanım yerleri değerle değil **rolle** adlandırılır.

## Renk Bir Değer Değil Bir Rol Taşır

`#275ea5` bir değerdir. "Birincil eylemin zemini" bir roldür. Aradaki fark, ikincisinin
arayüz değiştiğinde de doğru kalmasıdır.

Değerle adlandırılmış bir sistemde — "mavi", "açık mavi", "koyu mavi" — üç sorun çıkar.
Birincisi, aynı değer birbiriyle ilgisiz yerlerde kullanılır ve sonradan yalnızca birinin
değişmesi gerektiğinde ayrıştırılamaz. İkincisi, ton değiştiğinde bütün adlar yalan söyler;
"mavi" adlı sabit yeşile ayarlanır. Üçüncüsü, koyu temada değerler tersine döner ve "açık
mavi" adı koyu bir rengi gösterir. Koyu tema, bu kursun yedinci dersinin konusudur ve rol
adlandırmasının bedelini orada geri öder.

Rol adlandırması üç parçadan kurulur: neyin rengi (yüzey, metin, kenarlık, eylem), hangi
düzeyde (birincil, ikincil) ve gerekiyorsa neyin üzerinde (`-uzeri` eki). Bir rolün
üzerine düşen metnin rolü ayrıca adlandırılır, çünkü o metin zeminiyle birlikte denetlenir.

## Rampa Tek Tondan Türetilir

Bir aileyi oluşturan basamaklar tek tek seçilmez. Ton ve doygunluk sabit tutulur, yalnızca
açıklık değiştirilir. Bu yaklaşım M14/K02 CSS ile Görsel Sunum kursunda tanıtılmıştı; burada
üretilen rampanın her basamağının **bağıl parlaklık (relative luminance)** değeri de
hesaplanır, çünkü rolleri birbirine eşleştirmenin ölçütü budur.

```js
// rampa.mjs — tek tondan aciklik ekseninde renk rampasi ve rampanin bagil parlakligi

// HSL -> RGB (CSS renk gosterimi dersindeki donusumun aynisi)
function hslRgb(h, s, l) {
  s /= 100; l /= 100;
  const k = (n) => (n + h / 30) % 12;
  const a = s * Math.min(l, 1 - l);
  const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
  return [f(0), f(8), f(4)].map((v) => Math.round(v * 255));
}
const onaltilik = (rgb) => "#" + rgb.map((v) => v.toString(16).padStart(2, "0")).join("");

// WCAG bagil parlaklik ve kontrast orani
function kanal(v) {
  const s = v / 255;
  return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
}
const parlaklik = ([r, g, b]) => 0.2126 * kanal(r) + 0.7152 * kanal(g) + 0.0722 * kanal(b);
function kontrast(a, b) {
  const [x, y] = [parlaklik(a), parlaklik(b)].sort((p, q) => q - p);
  return (x + 0.05) / (y + 0.05);
}

const BASAMAK = [
  { ad: "000", l: 100 }, { ad: "050", l: 97 }, { ad: "100", l: 92 },
  { ad: "200", l: 84 }, { ad: "300", l: 74 }, { ad: "400", l: 62 },
  { ad: "500", l: 50 }, { ad: "600", l: 40 }, { ad: "700", l: 31 },
  { ad: "800", l: 22 }, { ad: "900", l: 14 },
];

const AILE = {
  notr:    { ton: 214, doygunluk: 8 },
  ana:     { ton: 214, doygunluk: 62 },
  ikincil: { ton: 268, doygunluk: 46 },
};

const rampa = {};
for (const [ad, { ton, doygunluk }] of Object.entries(AILE)) {
  rampa[ad] = {};
  for (const b of BASAMAK) {
    rampa[ad][b.ad] = hslRgb(ton, doygunluk, b.l);
  }
}

const BEYAZ = [255, 255, 255];
for (const ad of Object.keys(AILE)) {
  console.log(`\n--- ${ad} (ton ${AILE[ad].ton}, doygunluk ${AILE[ad].doygunluk}%) ---`);
  console.log("basamak  aciklik  onaltilik   bagil parlaklik  beyaza karsi");
  for (const b of BASAMAK) {
    const rgb = rampa[ad][b.ad];
    console.log(
      `${b.ad.padStart(7)} ${String(b.l).padStart(7)}%  ${onaltilik(rgb)}  ` +
        `${parlaklik(rgb).toFixed(4).padStart(15)}  ${kontrast(rgb, BEYAZ).toFixed(2).padStart(11)}:1`
    );
  }
}
```

```
--- notr (ton 214, doygunluk 8%) ---
basamak  aciklik  onaltilik   bagil parlaklik  beyaza karsi
    000     100%  #ffffff           1.0000         1.00:1
    050      97%  #f7f7f8           0.9307         1.07:1
    100      92%  #e9eaec           0.8223         1.20:1
    200      84%  #d3d6d9           0.6695         1.46:1
    300      74%  #b7bcc2           0.4993         1.91:1
    400      62%  #969da6           0.3335         2.74:1
    500      50%  #757e8a           0.2054         4.11:1
    600      40%  #5e656e           0.1281         5.89:1
    700      31%  #494e55           0.0752         8.39:1
    800      22%  #34383d           0.0390        11.80:1
    900      14%  #212327           0.0167        15.74:1

--- ana (ton 214, doygunluk 62%) ---
basamak  aciklik  onaltilik   bagil parlaklik  beyaza karsi
    000     100%  #ffffff           1.0000         1.00:1
    050      97%  #f3f7fc           0.9260         1.08:1
    100      92%  #dee9f7           0.8052         1.23:1
    200      84%  #bdd3ef           0.6364         1.53:1
    300      74%  #94b7e6           0.4588         2.06:1
    400      62%  #6296da           0.2947         3.05:1
    500      50%  #3075cf           0.1786         4.59:1
    600      40%  #275ea5           0.1115         6.50:1
    700      31%  #1e4980           0.0660         9.05:1
    800      22%  #15335b           0.0328        12.68:1
    900      14%  #0e213a           0.0149        16.19:1

--- ikincil (ton 268, doygunluk 46%) ---
basamak  aciklik  onaltilik   bagil parlaklik  beyaza karsi
    000     100%  #ffffff           1.0000         1.00:1
    050      97%  #f7f4fb           0.9144         1.09:1
    100      92%  #eae1f4           0.7787         1.27:1
    200      84%  #d5c3e9           0.5906         1.64:1
    300      74%  #bb9edb           0.4013         2.33:1
    400      62%  #9b72cb           0.2331         3.71:1
    500      50%  #7c45ba           0.1209         6.15:1
    600      40%  #633795           0.0755         8.36:1
    700      31%  #4d2b73           0.0454        11.00:1
    800      22%  #361e52           0.0232        14.34:1
    900      14%  #231334           0.0107        17.30:1
```

Üç aile aynı basamak adlarını taşıyor ve aynı açıklık değerlerinden üretildi. Buna rağmen
aynı basamaktaki renklerin bağıl parlaklıkları eşit değil. Beş yüzüncü basamakta nötr
0.2054, ana 0.1786, ikincil 0.1209 parlaklık taşıyor. Aradaki fark küçük değil: ikincil
ailenin beş yüzüncü basamağı, nötr ailenin aynı basamağından yaklaşık yüzde kırk daha
karanlık.

Bunun nedeni, HSL gösterimindeki açıklık ekseninin algısal olmamasıdır. Bağıl parlaklık
üç kanalı eşit ağırlıklandırmaz; yeşile 0.7152, kırmızıya 0.2126, maviye 0.0722 katsayı
verir. Mavi ağırlıklı bir ton aynı açıklık değerinde daha karanlık bir parlaklık üretir.

Bunun pratik sonucu şudur: **basamak numarası eşitliği, kontrast eşitliği anlamına
gelmez.** Bir rolü bir basamağa bağlamak yetmez; eşleşmenin gerçekten çalıştığı ayrıca
hesaplanmalıdır.

## Roller Basamaklara Bağlanır ve Eşleşmeler Denetlenir

Rol tablosu, arayüzün ihtiyaç duyduğu görevleri sayar ve her birine bir basamak verir.
Ardından hangi rolün hangi zemin üzerine düştüğü listelenir ve her eşleşme denetlenir.

```js
// roller.mjs — renk rollerinin rampa basamaklarina baglanmasi ve eslesme denetimi

function hslRgb(h, s, l) {
  s /= 100; l /= 100;
  const k = (n) => (n + h / 30) % 12;
  const a = s * Math.min(l, 1 - l);
  const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
  return [f(0), f(8), f(4)].map((v) => Math.round(v * 255));
}
const onaltilik = (rgb) => "#" + rgb.map((v) => v.toString(16).padStart(2, "0")).join("");
function kanal(v) {
  const s = v / 255;
  return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
}
const parlaklik = ([r, g, b]) => 0.2126 * kanal(r) + 0.7152 * kanal(g) + 0.0722 * kanal(b);
function kontrast(a, b) {
  const [x, y] = [parlaklik(a), parlaklik(b)].sort((p, q) => q - p);
  return (x + 0.05) / (y + 0.05);
}

const ACIKLIK = { "000": 100, "050": 97, 100: 92, 200: 84, 300: 74, 400: 62, 500: 50, 600: 40, 700: 31, 800: 22, 900: 14 };
const TON = { notr: [214, 8], ana: [214, 62], ikincil: [268, 46] };
const renk = (aile, basamak) => hslRgb(TON[aile][0], TON[aile][1], ACIKLIK[basamak]);

// Roller: her rol bir gorevi adlandirir, bir rengi degil.
const ROL = {
  "yuzey":              ["notr", "000"],
  "yuzey-ikincil":      ["notr", "050"],
  "kenarlik":           ["notr", "300"],
  "metin-birincil":     ["notr", "900"],
  "metin-ikincil":      ["notr", "600"],
  "eylem-ana":          ["ana", "600"],
  "eylem-ana-uzeri":    ["notr", "000"],
  "vurgu-soluk":        ["ana", "100"],
  "vurgu-soluk-uzeri":  ["ana", "800"],
  "eylem-ikincil":      ["ikincil", "600"],
};

console.log("rol                  aile     basamak  onaltilik");
for (const [ad, [aile, b]] of Object.entries(ROL)) {
  console.log(`${ad.padEnd(20)} ${aile.padEnd(8)} ${b.padStart(7)}  ${onaltilik(renk(aile, b))}`);
}

// Eslesmeler: hangi rol hangi zeminin uzerine dusuyor ve hangi esik gecerli.
const ESLESME = [
  ["metin-birincil", "yuzey", 4.5],
  ["metin-ikincil", "yuzey", 4.5],
  ["metin-ikincil", "yuzey-ikincil", 4.5],
  ["kenarlik", "yuzey", 3.0],
  ["eylem-ana-uzeri", "eylem-ana", 4.5],
  ["eylem-ana", "yuzey", 3.0],
  ["vurgu-soluk-uzeri", "vurgu-soluk", 4.5],
  ["eylem-ikincil", "yuzey", 3.0],
];

console.log("\non plan             zemin            kontrast  esik  sonuc");
for (const [on, zemin, esik] of ESLESME) {
  const k = kontrast(renk(...ROL[on]), renk(...ROL[zemin]));
  console.log(
    `${on.padEnd(19)} ${zemin.padEnd(15)} ${k.toFixed(2).padStart(7)}:1 ${esik.toFixed(1).padStart(5)}  ${k >= esik ? "gecti" : "KALDI"}`
  );
}

// Iki eylem rengi yalniz tonla ayriliyorsa, gri tonlamada birbirine karisir.
const a = renk(...ROL["eylem-ana"]);
const b = renk(...ROL["eylem-ikincil"]);
console.log(
  `\neylem-ana (${onaltilik(a)}) ile eylem-ikincil (${onaltilik(b)}) arasindaki kontrast: ${kontrast(a, b).toFixed(3)}:1`
);
console.log(`bagil parlakliklar: ${parlaklik(a).toFixed(4)} ve ${parlaklik(b).toFixed(4)}`);

// Ayni ayrimi aciklik ekseninde kurmak icin kac basamak gerekiyor?
console.log("\neylem-ikincil basamagi  kontrast (eylem-ana'ya karsi)");
for (const bas of ["200", "300", "400", "500", "600", "700", "800"]) {
  const k = kontrast(a, renk("ikincil", bas));
  console.log(`${bas.padStart(22)}  ${k.toFixed(3).padStart(7)}:1  ${k >= 3.0 ? "ayrilir" : "-"}`);
}

// Kenarlik rolu esigi gecmediyse: hangi notr basamak 3.0'i ilk saglar?
console.log("\nnotr basamak  yuzeye karsi kontrast  3.0 esigi");
for (const bas of ["200", "300", "400", "500", "600"]) {
  const k = kontrast(renk("notr", bas), renk(...ROL["yuzey"]));
  console.log(`${bas.padStart(12)}  ${k.toFixed(2).padStart(21)}:1  ${k >= 3.0 ? "gecti" : "KALDI"}`);
}
```

```
rol                  aile     basamak  onaltilik
yuzey                notr         000  #ffffff
yuzey-ikincil        notr         050  #f7f7f8
kenarlik             notr         300  #b7bcc2
metin-birincil       notr         900  #212327
metin-ikincil        notr         600  #5e656e
eylem-ana            ana          600  #275ea5
eylem-ana-uzeri      notr         000  #ffffff
vurgu-soluk          ana          100  #dee9f7
vurgu-soluk-uzeri    ana          800  #15335b
eylem-ikincil        ikincil      600  #633795

on plan             zemin            kontrast  esik  sonuc
metin-birincil      yuzey             15.74:1   4.5  gecti
metin-ikincil       yuzey              5.89:1   4.5  gecti
metin-ikincil       yuzey-ikincil      5.51:1   4.5  gecti
kenarlik            yuzey              1.91:1   3.0  KALDI
eylem-ana-uzeri     eylem-ana          6.50:1   4.5  gecti
eylem-ana           yuzey              6.50:1   3.0  gecti
vurgu-soluk-uzeri   vurgu-soluk       10.33:1   4.5  gecti
eylem-ikincil       yuzey              8.36:1   3.0  gecti

eylem-ana (#275ea5) ile eylem-ikincil (#633795) arasindaki kontrast: 1.287:1
bagil parlakliklar: 0.1115 ve 0.0755

eylem-ikincil basamagi  kontrast (eylem-ana'ya karsi)
                   200    3.966:1  ayrilir
                   300    2.794:1  -
                   400    1.753:1  -
                   500    1.058:1  -
                   600    1.287:1  -
                   700    1.693:1  -
                   800    2.206:1  -

notr basamak  yuzeye karsi kontrast  3.0 esigi
         200                   1.46:1  KALDI
         300                   1.91:1  KALDI
         400                   2.74:1  KALDI
         500                   4.11:1  gecti
         600                   5.89:1  gecti
```

Denetim iki bulgu üretti. Eşiklerin gerekçesi ve hangi ölçütten geldikleri altıncı dersin
konusudur; burada önemli olan denetimin sistem kurulurken yapılmasıdır.

## Birinci Bulgu: Kenarlık Rolü Eşiği Geçmiyor

Kenarlık rolü nötr üç yüzüncü basamağa bağlanmıştı ve yüzeye karşı 1.91:1 kontrast oranı
üretiyor. Eşik 3.0 olduğuna göre bu kenarlık, arayüzün bir bileşen sınırını ilan etmek için
kullandığı çizgiyi güvenilir biçimde göstermiyor.

Son tablo düzeltmenin ölçüsünü veriyor: nötr ailede eşiği geçen ilk basamak 500, oranı
4.11:1. Dört yüzüncü basamak 2.74 ile hâlâ altta kalıyor. Kenarlık rolü nötr 500'e taşınır.

Bu düzeltmenin bir yan etkisi var ve fark edilmesi gerekir: nötr 500 gözle bakıldığında
belirgin bir gri çizgidir, üç yüzüncü basamağın verdiği ince ayrımdan uzaktır. Kenarlığın
tek işi bileşen sınırını göstermekse ve o sınır zaten yüzey farkıyla ya da boşlukla
kuruluyorsa, doğru karar kenarlığı koyulaştırmak değil kaldırmaktır. Yakınlık ve Gruplama
dersindeki kural burada geri döner: çizgiyle kurulan bir ayrım, çoğu zaman boşlukla daha
ucuza kurulur. Denetim başarısız olduğunda seçenek iki tanedir — rolü eşiğe taşımak ya da
rolü gereksiz kılmak.

## İkinci Bulgu: İkinci Ton Bir Hiyerarşi Kanalı Değil

Ana eylem rengi ile ikincil eylem rengi aynı basamakta duruyor ve aralarındaki kontrast
oranı 1.287:1. Bu, iki rengin bağıl parlaklık ekseninde neredeyse aynı yerde olduğu
anlamına gelir. Renk ayrımı sınırlı olan bir okur ya da gri tonlamalı bir çıktı için bu iki
düğme aynı düğmedir.

Üçüncü tablo çözümün neden ton ekseninde olmadığını gösteriyor. İkincil aileyi ana eylemden
3.0 kontrastla ayırmak için 200 basamağına, yani açık bir mora inmek gerekiyor; ama o
basamak beyaz metin taşıyamaz, çünkü kendisi yüzeye 1.64:1 kontrastla oturuyor. Aradaki
basamakların hiçbiri ayrımı üretmiyor: 500 basamağında oran 1.058, yani iki renk pratikte
özdeş parlaklıkta.

Buradan çıkan kural, ilk dersteki iki kanal kuralının renk tarafındaki karşılığıdır:
**birincil ve ikincil eylem ton farkıyla ayrılmaz.** Ayrım yüzey kanalında kurulur —
birincil eylem dolgulu bir yüzey taşır, ikincil eylem yalnızca kenarlık ya da düz metin
olarak çizilir. Ton farkı bu ayrımın üzerine eklenebilir; ayrımı tek başına taşıyamaz.

İkinci tonun sistemde bir yeri vardır, ancak o yer eylem hiyerarşisi değildir: birbirinden
bağımsız kategorileri işaretlemek, bir bilgi türünü diğerinden ayırmak ya da vurgu
alanlarında zemin oluşturmak. Katalog arayüzünde ikincil aile, koleksiyon türünü gösteren
etiketlerde kullanılır; oradaki ayrım hiyerarşik değil, kategoriktir ve etiketin üzerindeki
metin ayrımı zaten taşır.

## Sistemin Yazılı Biçimi

Rampa ve roller iki ayrı katman olarak yazılır. Birinci katman ham basamaklardır, ikinci
katman rollerdir ve roller yalnızca basamaklara başvurur:

```css
:root {
  /* basamaklar */
  --notr-000: #ffffff;
  --notr-050: #f7f7f8;
  --notr-500: #757e8a;
  --notr-600: #5e656e;
  --notr-900: #212327;
  --ana-100: #dee9f7;
  --ana-600: #275ea5;
  --ana-800: #15335b;

  /* roller */
  --yuzey: var(--notr-000);
  --yuzey-ikincil: var(--notr-050);
  --kenarlik: var(--notr-500);
  --metin-birincil: var(--notr-900);
  --metin-ikincil: var(--notr-600);
  --eylem-ana: var(--ana-600);
  --eylem-ana-uzeri: var(--notr-000);
}
```

İki katmanın ayrı tutulması, bileşenlerin yalnızca rol adlarını kullanmasını sağlar. Bir
bileşen `--notr-600` yazdığı anda sistem delinmiştir: o bileşen artık ton değişikliğinden,
tema değişikliğinden ve rol yeniden atamasından etkilenmez. Rol adlarına bağlı kalan bir
bileşen ise bu değişikliklerin hepsini ücretsiz devralır.

## Özet

- Renk kararları değerle değil rolle adlandırılır; rol, arayüz değiştiğinde de doğru kalan
  addır ve tema geçişini mümkün kılan yapıdır.
- Bir renk ailesi tek tondan, açıklık ekseninde türetilir; basamak adları aileler arasında
  ortaktır.
- Aynı basamak numarası aynı kontrastı vermez; bağıl parlaklık kanalları eşit
  ağırlıklandırmadığı için ton değiştiğinde aynı açıklık farklı parlaklık üretir.
- Rol eşleşmeleri sistem kurulurken denetlenir; denetim başarısız olduğunda ya rol eşiğe
  taşınır ya da rolün gerekli olup olmadığı sorgulanır.
- Birincil ve ikincil eylem ton farkıyla ayrılmaz; iki ton aynı basamakta neredeyse özdeş
  parlaklık taşır ve gri tonlamada tek bir renge iner.
- Sistem iki katman olarak yazılır: ham basamaklar ve rol adları. Bileşenler yalnızca rol
  adlarına başvurur.

## Sonraki Adım

Bu ders rengi görsel bir göreve bağladı: yüzey, metin, kenarlık, eylem. Arayüzün rengi
kullandığı ikinci bir alan daha var ve orada renk görsel bir görev değil, doğrudan bir
**anlam** taşıyor. Ödünç alma işleminin başarıyla tamamlandığı, kaydın rafta bulunmadığı,
iade tarihinin geçtiği bilgisi arayüzde renkle bildiriliyor. Sonraki ders bu anlamların
hangi renklere bağlandığını, bağlamanın neden tek başına yeterli olmadığını ve her işlevsel
rengin kendi zemin ve metin çiftini nasıl taşıması gerektiğini ele alır.
