Uji Kontras Anda!

Jan 12 2023
Kita semua pernah melihat kombo warna itu di suatu tempat yang membuat kita berkata "ah, siapa yang mengira itu ide yang bagus?", dan ini bisa sangat mengganggu jika kita mengizinkan pengguna untuk memilih warna mereka sendiri, karena mereka dapat menyabotase diri mereka sendiri. Beberapa platform mengatasinya dengan membatasi warna pengguna ke kumpulan warna yang diketahui, tetapi ini tentu saja membatasi penyesuaian.

Kita semua pernah melihat kombo warna itu di suatu tempat yang membuat kita berkata "ah, siapa yang mengira itu ide yang bagus?", dan ini bisa sangat mengganggu jika kita mengizinkan pengguna untuk memilih warna mereka sendiri, karena mereka dapat menyabotase diri mereka sendiri. Beberapa platform mengatasinya dengan membatasi warna pengguna ke kumpulan warna yang diketahui, tetapi ini tentu saja membatasi penyesuaian.

Kami mengalami masalah ini baik dalam warna pengguna maupun warna kami sendiri, yang pada awalnya mengarahkan kami ke jajak pendapat seperti ini:

Pewarna Warna

Itu membutuhkan Orang Aktual, yang terbatas pada waktu, energi, dll - dan juga berarti proses iteratif yang relatif lambat. Ini bagus untuk kombo warna awal , tetapi tidak untuk memeriksa keterbacaan . Dan itu pasti tidak cocok untuk pengujian otomatis.

Kabar baiknya adalah bahwa Kontras yang Baik ADALAH Dapat Dikuantifikasi, dan karenanya Dapat Diuji, dan karenanya Dapat Diperbaiki!

Pedoman Aksesibilitas Konten Web - WCAG - memiliki definisi untuk kontras yang baik di sini , dapat dihitung per rasio kontras antara pencahayaan relatif dari dua warna.

Itu banyak kata, tapi tidak banyak kode!

Menghitung Kontras

Di Kotlin, fungsi Relative Luminance terlihat seperti ini:

/** All defined by https://www.w3.org/TR/WCAG20/#relativeluminancedef */
fun getRelativeLuminance(color:Color):Double{
   fun getRelativeChannelLuminance(channel:Float):Double =
           if (channel < 0.03928)  channel / 12.92
           else ((channel + 0.055) / 1.055).pow(2.4)

   val R = getRelativeChannelLuminance(color.r)
   val G = getRelativeChannelLuminance(color.g)
   val B = getRelativeChannelLuminance(color.b)

   return 0.2126 * R + 0.7152 * G + 0.0722 * B
}

/** https://www.w3.org/TR/WCAG20/#contrast-ratiodef */
fun getContrastRatio(color1:Color, color2:Color): Double { // ratio can range from 1 to 21
   val innerColorLuminance = getRelativeLuminance(color1)
   val outerColorLuminance = getRelativeLuminance(color2)

   return if (innerColorLuminance > outerColorLuminance) (innerColorLuminance + 0.05) / (outerColorLuminance + 0.05)
   else (outerColorLuminance + 0.05) / (innerColorLuminance + 0.05)
}

Menurut WCAG, rasio minimum untuk teks yang dapat dibaca adalah 3:1, dengan rasio yang baik adalah 4,5:1. Jadi dengan 20 baris kode, Anda sekarang dapat menguji keterbacaan pengguna!

Memperbaiki kontras

Karena kita sekarang dapat menghitung seperti apa 'kontras yang baik' dan 'kontras yang buruk', kita juga dapat memperbaiki warna yang ada secara otomatis untuk menjadikannya bentuk yang lebih baik!

Cara saya melakukannya adalah:

  • Ambil warna dasar
  • Cari tahu menggunakan pencahayaan relatif mana yang lebih terang dan mana yang lebih gelap
  • Setiap langkah, ambil versi warna cerah 5% lebih terang, dan versi warna gelap 5% lebih gelap, hingga Anda mencapai kombo warna yang memberikan rasio kontras yang baik
  • Sarankan warna baru tersebut kepada pengguna

if (constrastRatio < 3) {
    val innerColorLuminance = getRelativeLuminance(color1)
    val outerColorLuminance = getRelativeLuminance(color2)

    val lerpColor1:Color
    val lerpColor2:Color

    if (innerColorLuminance > outerColorLuminance) { // inner is brighter
        lerpColor1 = Color.WHITE
        lerpColor2 = Color.BLACK
    }
    else {
        lerpColor1 = Color.BLACK
        lerpColor2 = Color.WHITE
    }

    var text = "Colors do not contrast enough - combination is unreadable!"

    for (i in 1..10){
        val newColor1 = color1.cpy().lerp(lerpColor1, 0.05f *i)
        val newColor2 = color2.cpy().lerp(lerpColor2, 0.05f *i)

        if (getContrastRatio(newColor1, newColor2) > 3){
            // the "*255" is because users speak in 0–255 RGB, and the library speaks in 0–1 RGB
            text += "\n Suggested inner color: (${(newColor1.r*255).toInt()}, ${(newColor1.g*255).toInt()}, ${(newColor1.b*255).toInt()})"
            text += "\n Suggested outer color: (${(newColor2.r*255).toInt()}, ${(newColor2.g*255).toInt()}, ${(newColor2.b*255).toInt()})"
            break
        }
    }

Contoh diambil dari Unciv , di mana saya menerapkan "warna koreksi otomatis" sebagai fitur untuk mod

Sebelum (atas) dan sesudah (bawah)
Pada pandangan pertama, bagi saya ini terlihat seperti algoritme yang salah, bagian atas lebih menarik! Tetapi semakin saya melihatnya, semakin saya menyadari bahwa itu membuat mata saya semakin tegang

Kesimpulan

Teori warna selalu tampak seperti hal yang abstrak di mana orang yang memahami warna pelengkap hanya lebih ajaib dan barang-barang mereka Terlihat Lebih Baik. Itulah mengapa standar seperti Pedoman Aksesibilitas Konten Web sangat penting — mereka dibuat untuk dipahami oleh orang yang menerapkannya, yang berarti bahwa bahkan seseorang yang biasa-biasa saja di UI seperti saya dapat dengan mudah menerapkannya dan membuat hidup pengguna lebih mudah