Shanraq.org Shanraq.org
Өз логотипіңіз, өз атыңыз, өз белгішеңіз: блог сіздікі болады
IT

Go тілі: нөлден өз блогыңызға дейін 45-сабақ (барлығы 50)

Өз логотипіңіз, өз атыңыз, өз белгішеңіз: блог сіздікі болады

Go курсының қырық бесінші сабағы. Блог бөтен ат, бөтен белгі әрі бөтен түс тағып жүр — осы сабаққа дейін. Ат он бір үлгіден бір баптауға көшеді, логотип сіздің файлыңыз болады, палитра — тоғыз жол, ал белгіше сіздікі. Қоса контраст саналып, екі қатеміз түзетілді. SVG — 193 байт.

Не үшін керек

Блог жұмыс істейді, оқиды, іздейді, адамдарды кіргізеді — әрі әлі күнге бөтен оқу дайындамасы сияқты көрінеді. Оны біреуге көрсетер алдында өзіңіздікі қылған жөн, ал бұл — бір кештің ісі.

Мұнда төрт бөлек нәрсе бар, әрі оларды шатастырмау керек. Ат — жоғарғы жолақта, қойынды тақырыбында әрі төменгі жолақта тұратын мәтін. Логотип — аттың қасындағы сурет. Түстер — беттің фоны, оның үстіндегі мәтін, сілтемелер мен түймелер. Белгіше — браузер қойындыда әрі бетбелгіде салатын кішкентай нәрсе.

Әрқайсысы өз орнында тұрады, әрқайсысы өзінше ауысады.

Бірден тұтас

Жаңа қалта, go mod init sabaq42. Қасында — логотипі бар static/brand қалтасы:

package main

import (
	"bytes"
	"embed"
	"flag"
	"fmt"
	"html/template"
	"io/fs"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
)

//go:embed static
var files embed.FS

// page — үлгі көретін нәрсе. Блогтың аты бұған баптаудан келеді, белгілеуге
// жазылмайды: белгілеуге жазылған ат әр үлгіде бірден тұрады.
type page struct {
	SiteName string
	Title    string
}

const layout = `<!doctype html>
<html lang="kk">
  <head>
    <title>{{ .Title }}{{ .SiteName }}</title>
    <link rel="icon" href="/static/brand/logo.svg" type="image/svg+xml">
    <link rel="apple-touch-icon" href="/static/brand/apple-touch-icon.png">
  </head>
  <body>
    <header>
      <a href="/"><img src="/static/brand/logo.svg" width="40" height="40" alt=""></a>
      <span>{{ .SiteName }}</span>
    </header>
  </body>
</html>`

func main() {
	name := flag.String("name", "Менің блогым", "блогтың аты")
	flag.Parse()

	tpl := template.Must(template.New("layout").Parse(layout))

	fmt.Println("== бүкіл бетке бір ат")
	tpl.Execute(os.Stdout, page{SiteName: *name, Title: "Мақалалар"})
	fmt.Println()

	// Сол үлгі, басқа ат: бетті түгел қайталамас үшін өзгерген жолдарды ғана
	// басып шығарамыз.
	fmt.Println("== басқа ат — сол үлгі")
	var buf bytes.Buffer
	tpl.Execute(&buf, page{SiteName: "Дала блогы", Title: "Мақалалар"})
	for _, line := range strings.Split(buf.String(), "\n") {
		if strings.Contains(line, "<title>") || strings.Contains(line, "<span>") {
			fmt.Println(strings.TrimSpace(line))
		}
	}
	fmt.Println()

	fmt.Println("== браузер өзі сұрайтын нәрсе")
	bare := httptest.NewServer(routes(false))
	defer bare.Close()
	fmt.Printf("%-30s %s\n", "/favicon.ico жауапсыз:", get(bare.URL+"/favicon.ico"))

	srv := httptest.NewServer(routes(true))
	defer srv.Close()
	fmt.Printf("%-30s %s\n", "/favicon.ico жауаппен:", get(srv.URL+"/favicon.ico"))
	fmt.Printf("%-30s %s\n", "/static/brand/logo.svg:", get(srv.URL+"/static/brand/logo.svg"))
	fmt.Printf("%-30s %s\n", "apple-touch-icon.png:", get(srv.URL+"/static/brand/apple-touch-icon.png"))

	fmt.Println()
	fmt.Println("== сурет көлемі")
	svg, _ := fs.ReadFile(files, "static/brand/logo.svg")
	fmt.Printf("logo.svg: %d байт, әрі бұл мәтін: %s…\n", len(svg),
		strings.SplitN(string(svg), ">", 2)[0][:38])
}

func routes(withIcon bool) http.Handler {
	mux := http.NewServeMux()
	mux.Handle("GET /static/", http.FileServerFS(files))

	// Браузер /favicon.ico-ны белгілеуде ол туралы бір сөз болмаса да өзі
	// сұрайды. Жауапсыз ол 404 алады — әрі бұл әр оқырманға журналға жазылады.
	if withIcon {
		mux.HandleFunc("GET /favicon.ico", func(w http.ResponseWriter, r *http.Request) {
			http.Redirect(w, r, "/static/brand/logo.svg", http.StatusMovedPermanently)
		})
	}
	return mux
}

func get(url string) string {
	resp, err := http.Get(url)
	if err != nil {
		return "қате: " + err.Error()
	}
	defer resp.Body.Close()
	return fmt.Sprintf("%d %s", resp.StatusCode, resp.Header.Get("Content-Type"))
}

Шығатыны:

== бүкіл бетке бір ат
<!doctype html>
<html lang="kk">
  <head>
    <title>Мақалалар — Менің блогым</title>
    <link rel="icon" href="/static/brand/logo.svg" type="image/svg+xml">
    <link rel="apple-touch-icon" href="/static/brand/apple-touch-icon.png">
  </head>
  <body>
    <header>
      <a href="/"><img src="/static/brand/logo.svg" width="40" height="40" alt=""></a>
      <span>Менің блогым</span>
    </header>
  </body>
</html>
== басқа ат — сол үлгі
<title>Мақалалар — Дала блогы</title>
<span>Дала блогы</span>

== браузер өзі сұрайтын нәрсе
/favicon.ico жауапсыз:         404 text/plain; charset=utf-8
/favicon.ico жауаппен:         200 image/svg+xml
/static/brand/logo.svg:        200 image/svg+xml
apple-touch-icon.png:          200 image/png

== сурет көлемі
logo.svg: 193 байт, әрі бұл мәтін: <svg xmlns="http://www.w3.org/2000/svg…

Талдау

Блогтың аты — белгілеудегі мәтін емес, баптау

Үлгіде «Менің блогым» деген сөз жоқ. {{ .SiteName }} бар, ал аттың өзі жалаушамен келеді:

name := flag.String("name", "Менің блогым", "блогтың аты")

Айырмашылық шығыстың екінші блогында көрінеді: сол үлгі, басқа ат — әрі ат кездесетін екі жол да өзгерді. Мысалда олар екеу, блогта — он бір файл, ал түзету бәрібір біреу.

Ат белгілеуге жазылып тұрғанда ол әр үлгіде тұрады: жоғарғы жолақ, төменгі жолақ, әр беттің <title>-ы. Біздің блогта ондай файл он бір — бәрін түзетуге тура келеді, әрі оныншысында бір күні ұмытады. Баптау бұл қателер тобын түбегейлі жояды — конфигурация сабағындағыдай, онда дерекқор мекенжайы дәл солай көшкен еді.

Блогта ат үлгілерге кірген адаммен бір жерде түседі: render оны бет деректеріне бір рет салады, әрі бірде-бір өңдеуші бұл туралы ойламайды.

Елестетіп көріңіз. Қабырғаға бояумен жазылған жазу емес, есіктегі тақтайша. Тақтайшаны бір минутта ауыстырады, бояуды бүкіл қабырғамен қайта жағады.

Логотип: SVG — бұл мәтін

Шығыстың соңғы жолы: біздің логотип 193 байт тартады әрі <svg дегеннен басталады. Бұл әдеттегі мағынадағы сурет емес, белгілеу — HTML-дегі сол бұрышты жақшалар, тек олар шеңбер мен үшбұрышты сипаттайды.

Осыдан белгі үшін дәл SVG алынатын үш салдар шығады:

  • ол бұлдырламайды: сол бір жол қойындыдағы 16 пиксельде де, телефон экранындағы 512-де де салынады;
  • ол кішкентай: сол сападағы PNG-нің ондаған килобайтына қарсы екі жүз байт;
  • оны мәтінмен түзетуге болады: редакторда ашып, түсін ауыстыру үшін графикалық бағдарлама керек емес.

Өз белгіңізді қою үшін өз файлыңызды static/brand/logo.svg орнына салыңыз — үлгілердегі атты өзгертудің қажеті жоқ. Белгіңіз PNG-де болса да қалыпты: қасына салып, src-ті түзетіңіз, бірақ width пен height-ты міндетті түрде қалдырыңыз, әйтпесе сурет жүктелгенде бет секіреді.

Түстер: бүкіл блогқа тоғыз жол

Белгіні ауыстырдық, ал бет әлі бөтен: сол қызыл сілтеме, сол кілегей фон. Блогтың түсі стиль файлының ең басында тұрады:

:root {
  --ink: #1a1a1a;     /* мәтін */
  --paper: #fffdf8;   /* беттің фоны */
  --accent: #d32f2f;  /* сілтемелер мен түймелер */
  --muted: #6b625a;   /* қолтаңбалар */
  --faint: #7a7169;   /* төменгі жолақ пен нұсқаулар */
  --line: #e6e0d6;    /* жоғарғы жолақтың астындағы және төменгі жолақтың үстіндегі сызықтар */
  --edge: #9d8f7b;    /* енгізу өрісінің жиегі */
  --field: #ffffff;   /* өріс немен боялған */
  --mark: #ffe08a;    /* іздеуде табылған сөздер */
}

Бұл — CSS айнымалылары: --ink — ат, #1a1a1a — мән, ал төмендегі кез келген ереже мәнді var(--ink) арқылы алады. Браузер мұны өзі істей алады, ешқандай жинақтауыш та, препроцессор да керек емес.

Басты түс — үшеу, әрі солардан бастайды: --paper — беттің фоны, --ink — оның үстіндегі мәтін, --accent — сілтемелер мен түймелер. Қалған алтауы — сол түстердің реңктері: қолтаңбалар, сызықтар, өріс жиегі.

31-ші қадамда мұны санап шықтым: палитраға 17 рет жүгінеді, жариялау — тоғыз, ал палитрадан тыс жазылған түс — нөл. Демек, --accent бір жерде өзгереді, әрі онымен бірге сілтемелер, түйме, түйменің жиегі мен қате мәтіні өзгереді. Айнымалысыз #d32f2f дегенді бүкіл файлдан іздеп, бір жерін таппай қалуға тура келер еді.

Түсті көзбен шамалап алуға болмайды

Әдемі түс пен оқылатын түс — екі бөлек нәрсе, әрі айырмашылығы саналады. Ереже контраст деп аталады: мәтін жарықтығының фон жарықтығына қатынасы. Кәдімгі мәтінге 4.5:1-ден кем болмауы керек, ірі мәтін мен көрінуге тиіс жиектерге — 3:1.

Бұл — осы сабақтың екінші кішкентай бағдарламасы, go mod init sabaq42b. Ол контрастты WCAG ережесі бойынша санайды әрі түс жарай ма, жоқ па деп айтады:

package main

import (
	"fmt"
	"math"
	"strconv"
	"strings"
)

// Блогтың палитрасы: беттегі әр түс алынатын тоғыз жол.
var palette = map[string]string{
	"--ink":    "#1a1a1a", // мәтін
	"--paper":  "#fffdf8", // беттің фоны
	"--accent": "#d32f2f", // сілтемелер мен түймелер
	"--muted":  "#6b625a", // қолтаңбалар
	"--faint":  "#7a7169", // төменгі жолақ пен нұсқаулар
	"--edge":   "#9d8f7b", // өріс жиегі
}

// pair — не нәрсенің үстінде не жазылған. WCAG нормасы: кәдімгі мәтінге
// 4.5:1, ірі мәтін мен көрінуге тиіс жиектерге 3:1.
type pair struct {
	what string
	fg   string
	bg   string
	need float64
}

func main() {
	fmt.Println("== біздің палитра")
	check([]pair{
		{"беттегі мәтін", palette["--ink"], palette["--paper"], 4.5},
		{"сілтеме", palette["--accent"], palette["--paper"], 4.5},
		{"өріс астындағы жазу", palette["--muted"], palette["--paper"], 4.5},
		{"төменгі жолақ", palette["--faint"], palette["--paper"], 4.5},
		{"түймедегі мәтін", palette["--paper"], palette["--accent"], 4.5},
		{"енгізу өрісінің жиегі", palette["--edge"], palette["--paper"], 3},
	})

	fmt.Println()
	fmt.Println("== осы сабаққа дейін қадамдарда не болды")
	check([]pair{
		{"төменгі жолақ", "#857c72", palette["--paper"], 4.5},
		{"енгізу өрісінің жиегі", "#ddd5c9", palette["--paper"], 3},
	})

	fmt.Println()
	fmt.Println("== әдемі, бірақ оқылмайды")
	check([]pair{
		{"ашық сұр сілтеме", "#9aa0a6", "#ffffff", 4.5},
		{"ақтағы сары", "#ffd400", "#ffffff", 4.5},
		{"қағаздағы көк", "#1a56db", palette["--paper"], 4.5},
	})
}

func check(pairs []pair) {
	for _, p := range pairs {
		r := contrast(p.fg, p.bg)
		verdict := "аз, керегі " + strconv.FormatFloat(p.need, 'g', -1, 64) + ":1"
		if r >= p.need {
			verdict = "жарайды"
		}
		fmt.Printf("%-24s %s / %s  %5.2f:1  %s\n", p.what, p.fg, p.bg, r, verdict)
	}
}

// contrast — WCAG ережесі бойынша жарықтық қатынасы: ашығырағы қосу 0,05-ті
// күңгірттеуіне қосу 0,05-ке бөлу. Бір — түстер бірдей, жиырма бір — ақтағы қара.
func contrast(a, b string) float64 {
	la, lb := luminance(a), luminance(b)
	if la < lb {
		la, lb = lb, la
	}
	return (la + 0.05) / (lb + 0.05)
}

// luminance — түсте қанша жарық бар. Адам көзі жасылды қызылдан ашығырақ
// көреді, ал көкті бәрінен күңгірт көреді, сондықтан салмақтары әртүрлі.
func luminance(hex string) float64 {
	h := strings.TrimPrefix(hex, "#")
	r := channel(h[0:2])
	g := channel(h[2:4])
	b := channel(h[4:6])
	return 0.2126*r + 0.7152*g + 0.0722*b
}

// channel бір құрауышты сызықтық түрге аударады: экран түсті санға тепе-тең
// көрсетпейді, әрі осы түзетусіз есеп қате шығады.
func channel(s string) float64 {
	v, err := strconv.ParseUint(s, 16, 8)
	if err != nil {
		return 0
	}
	c := float64(v) / 255
	if c <= 0.03928 {
		return c / 12.92
	}
	return math.Pow((c+0.055)/1.055, 2.4)
}

Шығатыны:

== біздің палитра
беттегі мәтін            #1a1a1a / #fffdf8  17.12:1  жарайды
сілтеме                  #d32f2f / #fffdf8   4.90:1  жарайды
өріс астындағы жазу      #6b625a / #fffdf8   5.87:1  жарайды
төменгі жолақ            #7a7169 / #fffdf8   4.70:1  жарайды
түймедегі мәтін          #fffdf8 / #d32f2f   4.90:1  жарайды
енгізу өрісінің жиегі    #9d8f7b / #fffdf8   3.11:1  жарайды

== осы сабаққа дейін қадамдарда не болды
төменгі жолақ            #857c72 / #fffdf8   4.03:1  аз, керегі 4.5:1
енгізу өрісінің жиегі    #ddd5c9 / #fffdf8   1.43:1  аз, керегі 3:1

== әдемі, бірақ оқылмайды
ашық сұр сілтеме         #9aa0a6 / #ffffff   2.64:1  аз, керегі 4.5:1
ақтағы сары              #ffd400 / #ffffff   1.43:1  аз, керегі 4.5:1
қағаздағы көк            #1a56db / #fffdf8   6.08:1  жарайды

Осы шығыста үш нәрсені көрген жөн.

Біздің палитра өтеді — бірақ қормен емес: сілтеме 4.90:1, ал норма 4.5:1. Қызылды сәл ашықтау қылсаңыз — телефон экранына күн түскенде сілтемені оқу қиындайды.

Екінші блок — біздің өз қатеміз. Осы сабаққа дейін қадамдардағы төменгі жолақ #857c72 болатын — 4.03:1, ұсақ мәтін нормасынан төмен. Ал енгізу өрісінің жиегі #ddd5c9 1.43:1 беретін: өріс мүлде дерлік сызылмаған еді, әрі нашар экранда оның шеті жоғалатын. Екі түс те қадамдарда түзетілді — енді 4.70:1 мен 3.11:1. Санағанша мұны байқамадым; көзбен бәрі дұрыс көрінген.

Үшінші блок — талғам туралы. Ашық сұр сілтеме #9aa0a6 2.64:1 береді, ал ақтағы сары — 1.43:1. Дәл сол «макетте әдемі көрінетін» әрі көшеде оқылмайтын сайттар. Ал біздің қағазымыздағы көк #1a56db6.08:1, оны батыл алуға болады.

Блогты түсі жағынан өзіңіздікі қылу үшін үш жолды өзгертіңіз әрі бағдарламамен тексеріңіз:

--paper: #f7f7f5;   /* кілегейдің орнына салқын қағаз */
--ink: #14181f;
--accent: #1a56db;  /* қызылдың орнына көк */

Елестетіп көріңіз. Дуалдың әр тақтайын бөлек бояу емес, бояу банкасын ауыстыру. Барлық тақтай өзі қайта боялады — әрі бірде-бірі ескі түсімен қалмайды.

Қараңғы тема — сол нәрсенің қайталануы. Браузер адамның жүйеде қараңғы безендіруді таңдағанын хабарлайды, әрі сол айнымалыларды қайта анықтау жеткілікті:

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #14120f;
    --ink: #efe9df;
    --accent: #ff6b6b;
  }
}

Төмендегі бірде-бір ережені тиюдің қажеті жоқ: олар түсті палитрадан алады. Қараңғы темадағы контраст сол бағдарламамен саналады — әрі әдетте қызылды ашықтау қылуға тура келеді, әйтпесе қараның үстінде ол «отырып қалады».

Қойындыдағы белгіше: браузер оны өзі сұрайды

Шығыстың үшінші блогы — көпті таңқалдыратын өлшенген факт:

/favicon.ico жауапсыз:  404
/favicon.ico жауаппен:  200 image/svg+xml

Браузер /favicon.ico-ны белгілеуде ол туралы бір сөз болмаса да өзі сұрайды. Жауап бермесеңіз — әр оқырманға журналда 404, ал қойындыда бос шаршы.

Жауапты екі жолмен беруге болады:

<link rel="icon" href="/static/brand/logo.svg" type="image/svg+xml">

әрі /favicon.ico-дан сол файлға бағыттаумен — мысалдағыдай. Біріншісі браузерге белгіше қайда екенін айтады; екіншісі ескі әдетпен сұрағандарды ұстайды.

Бір SVG аздық етеді: apple-touch-icon

SVG белгіше ретінде барлық қазіргі браузерге түсінікті, бір жағдайдан басқа: бетті iPhone-ның негізгі экранына қосқанда. Онда шаршы PNG керек:

<link rel="apple-touch-icon" href="/static/brand/apple-touch-icon.png">

Өлшемі — 180×180, мөлдірліксіз (iOS бәрібір өз фонын қояды), шеттерінде шағын алаңмен: жүйе бұрыштарын кеседі. Мысалда ол белгінің қасында жатыр әрі 200 image/png деп жауап береді — бір файл, әрі блог бөтен телефонда атсыз көрінуін қояды.

Белгішені браузер ұзақ есте сақтайды

Бөлек білген жөн: белгішені бәрінен қатаң кэштейді. Файлды ауыстырдыңыз, бетті жаңарттыңыз — қойындыда әлі ескісі, әрі бұл қалыпты.

Емі бақсылық емес, ат: файлды жаңа атпен сақтаңыз (logo-2.svg) әрі сілтемені түзетіңіз. Статика сабағындағы стильдермен бірдей тәсіл, әрі ол «кэшті тазалаңыз» дегеннен өзгеше — әрқашан жұмыс істейді.

Сіздің атыңызды тағы не тағып жүр

Үлгілердің ішінде жүргенде, оқырман емес, машина көретінді де түзетіп кетіңіз:

  • <html lang="kk"> — беттің тілі. Дұрысы тұрсын, әйтпесе браузер бетті басқа тілден аударуды ұсынады;
  • <title> — қойындыда әрі іздеу нәтижесінде көрінетін нәрсе. Мысалдағы «Бет — Блогтың аты» пішімі барлық бетке бір ат қойғаннан жақсы оқылады;
  • <meta name="description"> — іздеудегі сілтеменің астындағы жол. Ол бізде әлі жоқ, әрі бұл — тексеру парағы сабағының қарызы.

Сабақ картасы

Сабақ картасы: ат, белгі және белгіше

Өз сөзіңізбен айтыңыз

Қарамай, дауыстап немесе қағазға жауап беріңіз. Жауаптар — сабақтың соңында.

  1. Блогтың атын неге үлгілерде емес, баптауда ұстайды?
  2. Түсті неге керек әр ережеде емес, палитрада жазады?
  3. Кәдімгі мәтінге қандай контраст керек әрі оны неге көзбен анықтауға болмайды?
  4. /favicon.ico-ға жауап бермесе не болады?
  5. Белгі үшін неге PNG емес, SVG алады?

Тапсырма

Міндетті. Блогты өзіңіздікі қылыңыз: ат -name жалаушасынан немесе BLOG_NAME айнымалысынан келеді әрі барлық үлгіге бірден түседі, сіздің логотипіңіз static/brand/logo.svg-де жатыр, өз үш түсіңіз :root-та тұр, ал /favicon.ico үндемей емес, жауап беріп тұрады. Фондағы мәтін мен фондағы сілтеменің контрастын сабақтағы бағдарламамен тексеріңіз: 4,5:1-ден төмен болса — палитраға қайта оралыңыз.

Мұның бәрі step-30 ішінде жасалған — өзіңіз жасағаннан кейін салыстырыңыз.

Қалауыңызша.

  • Өз белгіңізді редакторда сызыңыз: шеңбер, әріп, екі сызық. SVG — бұл мәтін, біздің файлдың көшірмесінен бастауға болады.
  • Қараңғы тема қосыңыз: @media (prefers-color-scheme: dark) әрі сол айнымалылар басқа мәндермен. Ондағы контрастты да тексеріңіз — қара фонда түстер өзгеше ұстанады.
  • 180×180 apple-touch-icon.png қосып, блогтың телефонның негізгі экранында қалай көрінетінін тексеріңіз.
  • Блогтың аты <title>-ға да, төменгі жолаққа да, RSS тақырыбына да бір жерден түсетін қылыңыз.

Блогта бұл қайда тұрады

Біздің блогта ат он бір файлда тұрған: әр беттің тақырыбында әрі төменгі жолақта. Енді ол баптаумен келеді: -name "Дала блогы" деп іске қосу қойынды тақырыбын да, төменгі жолақтағы жолды да ауыстырады, ал render атты бет деректеріне бір рет салады — бірде-бір өңдеуші бұл туралы есінде ұстамайды.

Қадамдардағы түстер де енді бір жерде: палитраға 17 рет жүгінеді, жариялау тоғыз, ал палитрадан тыс түс бірде-бірі қалмады. Қоса контраст санағыштың екі олжасы түзетілді — төменгі жолақ 4,03 → 4,70 әрі өріс жиегі 1,43 → 3,11.

30-шы қадамда белгішенің үшеуі де жауап береді:

/favicon.ico                       301 -> /static/brand/logo.svg
/static/brand/logo.svg             200 image/svg+xml 1143 байт
/static/brand/apple-touch-icon.png 200 image/png     2092 байт

Блог бөтен дайындама болуын қояды. Әрі қарай оны адамдарға көрсетуге болады — келесі сабақтар дәл соны қалай істеу керегі туралы.

Қарыздар. Беттерде іздеу жүйесіне арналған сипаттама әлі жоқ. Әлеуметтік желіге арналған сурет — сілтеме жібергенде көрінетіні — де жоқ. Әрі автордың аты еш жерде шықпайды, дерекқорда ол бар болса да.

Жауаптар

Жауаптарды көрсету
  1. Өйткені белгілеуде ол әр үлгіде тұрады — жоғарғы жолақ, төменгі жолақ, әр қойындының тақырыбы — әрі бір күні оны бәрінде ауыстырмайды. Баптау оны бір жерде ұстайды, ал үлгілер дайынын алады.
  2. Өйткені бір түс бірнеше ережеде керек: сілтеме, түйме, оның жиегі, қате мәтіні. Палитрада ол бір рет жарияланады әрі бір рет өзгереді; әйтпесе оны бүкіл файлдан іздеп, бір жерін таппай қалуға тура келеді.
  3. 4,5:1-ден кем емес. Көзбен анықтауға болмайды, өйткені көз жарықтықты емес, түстерді салыстырады: біздің төменгі жолағымыз дұрыс көрінген еді, ал 4,03:1 берген — нормадан төмен, әрі мұны тек санағанда көрдік.
  4. Браузер оны бәрібір сұрайды әрі 404 алады: журналда әр оқырманға қате пайда болады, ал қойындыда белгінің орнына бос шаршы тұрады.
  5. Өйткені SVG — кез келген өлшемге бұлдырламай салынатын, ондаған килобайттың орнына жүздеген байт тартатын әрі кәдімгі редакторда түзетілетін мәтін.

Дереккөздер

Мәтінде қате не теру қатесі кездессе, бізге айтыңыз

Тапсырманы тексеру

Алдымен VS Code-та шешіп, іске қосыңыз — редактор қатені сол жерде көрсетеді. Дайын шешімді осында қойыңыз. Тексеретін — модель: ол қатені атап көрсетеді, бірақ дайын жауапты бермейді.

Тексеру үшін кіру керек. Кіру

Пікірлер (0)

Әзірге пікір жоқ. Бірінші болыңыз.