Стилизация осей через CSS

Оси в D3.js формируются как набор SVG-элементов, создаваемых генераторами d3-axis. При вызове axisBottom, axisTop, axisLeft, axisRight библиотека не рисует «ось» как единый объект, а собирает её из примитивов SVG:

  • основной линии домена (domain line)
  • делений (ticks)
  • подписей делений (tick labels)
  • вспомогательных групп <g>

Базовая структура DOM после рендера оси выглядит следующим образом:

<g class="x-axis">
  <path class="domain"></path>
  <g class="tick">
    <line></line>
    <text></text>
  </g>
  <g class="tick">
    <line></line>
    <text></text>
  </g>
</g>

Каждый элемент получает предсказуемые CSS-классы, что делает стилизацию полностью отделённой от логики построения шкалы.


Классы и семантика элементов оси

D3.js использует фиксированную систему классов, которую можно считать контрактом между генератором осей и CSS-стилями.

Основные классы

  • .domain — линия, отображающая базовую ось
  • .tick — контейнер одного деления
  • .tick line — линия деления
  • .tick text — подпись деления
  • .axis — общий контейнер оси (задаётся вручную разработчиком)

Дополнительно часто применяются кастомные классы:

  • .x-axis, .y-axis
  • .grid
  • .minor-tick, .major-tick

Эти классы не создаются автоматически библиотекой, но добавляются через .attr("class", ...).


Базовая стилизация осей через CSS

Стилизация начинается с переопределения стандартного внешнего вида SVG-элементов. По умолчанию оси наследуют стили документа и выглядят нейтрально, без акцентов.

.axis path.domain {
  stroke: #333;
  stroke-width: 1;
  shape-rendering: crispEdges;
}

.axis .tick line {
  stroke: #999;
  stroke-width: 1;
}

.axis .tick text {
  fill: #333;
  font-size: 12px;
  font-family: sans-serif;
}

Ключевой момент заключается в том, что SVG не использует привычные свойства CSS-блочной модели. Вместо этого применяются:

  • stroke вместо border
  • fill вместо color (для текста и фигур)
  • stroke-width вместо толщины рамки

Управление доменной линией

Домена линия (axis baseline) часто требует отдельной стилизации, так как визуально определяет границу графика.

.x-axis path.domain {
  stroke: #000;
  stroke-opacity: 0.2;
  shape-rendering: geometricPrecision;
}

Удаление доменной линии также выполняется через CSS:

.axis path.domain {
  display: none;
}

Альтернативный подход — оставить линию, но визуально ослабить её:

.axis path.domain {
  stroke: #ccc;
  stroke-dasharray: 2 2;
}

Стилизация делений (ticks)

Каждое деление состоит из линии и текста. Эти элементы можно стилизовать независимо, создавая визуальную иерархию.

.axis .tick line {
  stroke: #e0e0e0;
  stroke-height: 6;
}

Управление длиной делений часто комбинируется с параметрами генератора оси:

d3.axisBottom(scale)
  .tickSize(6)
  .tickPadding(8);

CSS при этом отвечает только за внешний вид, но не за геометрию.


Подавление и акцентирование делений

Часто требуется различать основные и второстепенные деления. Это достигается через кастомные классы:

axis.selectAll(".tick")
  .classed("minor-tick", (d) => d % 5 !== 0)
  .classed("major-tick", (d) => d % 5 === 0);

CSS:

.minor-tick line {
  stroke: #eee;
}

.major-tick line {
  stroke: #999;
  stroke-width: 1.2;
}

Такой подход позволяет полностью управлять визуальной иерархией без изменения логики шкалы.


Стилизация подписей осей

Текстовые элементы являются наиболее чувствительными к типографике части осей.

.axis .tick text {
  fill: #444;
  font-size: 11px;
  font-family: "Roboto", sans-serif;
  text-anchor: middle;
}

Часто используется поворот подписей для плотных осей:

g.call(axis)
  .selectAll("text")
  .attr("transform", "rotate(-45)")
  .style("text-anchor", "end");

CSS может дополнять трансформации:

.axis .tick text {
  dominant-baseline: hanging;
}

Скрытие элементов оси через CSS

Иногда необходимо полностью убрать визуальные части оси, сохранив структуру:

.axis .tick line {
  display: none;
}

Или более тонкая настройка:

.axis .tick text {
  opacity: 0.5;
}

Полное отключение оси возможно через:

.x-axis {
  display: none;
}

Работа с цветами и темами

Стилизация осей часто зависит от общей темы графика. Использование CSS-переменных упрощает адаптацию:

:root {
  --axis-color: #333;
  --tick-color: #aaa;
  --label-color: #222;
}

.axis path.domain {
  stroke: var(--axis-color);
}

.axis .tick line {
  stroke: var(--tick-color);
}

.axis .tick text {
  fill: var(--label-color);
}

Темная тема:

.dark-theme {
  --axis-color: #ccc;
  --tick-color: #555;
  --label-color: #eee;
}

Гриды как продолжение осей

Сетка часто реализуется через повторное использование осей с изменённой стилизацией:

d3.axisLeft(yScale)
  .tickSize(-width)
  .tickFormat("");

CSS для сетки:

.grid line {
  stroke: #eee;
  stroke-opacity: 0.7;
  shape-rendering: crispEdges;
}

.grid path {
  display: none;
}

Грид-оси обычно отделяются от основных:

svg.append("g")
  .attr("class", "grid y-grid")
  .call(yGrid);

Тонкая настройка визуального рендеринга SVG

SVG-оси чувствительны к рендерингу в браузере, поэтому используются дополнительные свойства:

.axis line,
.axis path {
  shape-rendering: crispEdges;
}

Для более гладких линий:

.axis path.domain {
  shape-rendering: geometricPrecision;
}

Выбор режима влияет на визуальную стабильность при масштабировании.


Комбинирование CSS и атрибутов D3

CSS не заменяет атрибутную стилизацию, а дополняет её. Часто используется гибридный подход:

g.call(d3.axisBottom(xScale))
  .attr("class", "axis x-axis");

И CSS:

.x-axis {
  font-variant-numeric: tabular-nums;
}

Атрибуты SVG:

.attr("stroke", "#999")
.attr("fill", "none");

Используются для динамических значений, которые зависят от данных, тогда как CSS отвечает за общую визуальную систему.


Поведение при масштабировании и responsive-оси

При изменении размеров графика оси перерисовываются, но CSS сохраняется неизменным. Это позволяет отделить:

  • вычислительную модель (D3)
  • визуальную систему (CSS)

Пример адаптивной настройки:

function resize(width) {
  xAxis.scale(xScale.range([0, width]));
  svg.select(".x-axis").call(xAxis);
}

CSS при этом остаётся стабильным:

.axis .tick text {
  font-size: 10px;
}

Управление перекрытием подписей

При плотных данных подписи оси могут перекрываться. CSS позволяет частично решать проблему:

.axis .tick text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

Хотя SVG ограниченно поддерживает текстовые overflow-правила, комбинация с JavaScript-трансформациями даёт более стабильный результат.


Каскадность и приоритеты стилей

Стили SVG подчиняются специфике каскада:

  1. inline-атрибуты SVG (attr("fill"))
  2. стили CSS с высокой специфичностью
  3. глобальные стили документа

Пример конфликта:

.attr("fill", "red")
.tick text {
  fill: black;
}

В этом случае inline-значение имеет приоритет.

Для управления приоритетом используется:

.tick text {
  fill: black !important;
}

Организация масштабируемой системы стилей

В сложных визуализациях стили осей выносятся в отдельный слой абстракции:

  • базовые стили осей
  • стили конкретных графиков
  • тематические переопределения
  • состояния (hover, focus, selection)

Пример структуры:

.axis {
  font-family: sans-serif;
}

.axis--compact .tick text {
  font-size: 9px;
}

.axis--large .tick text {
  font-size: 14px;
}

Такой подход позволяет масштабировать визуальную систему без изменения логики D3-кода.