Оси в D3.js формируются как набор SVG-элементов, создаваемых
генераторами d3-axis. При вызове axisBottom,
axisTop, axisLeft, axisRight
библиотека не рисует «ось» как единый объект, а собирает её из
примитивов SVG:
<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", ...).
Стилизация начинается с переопределения стандартного внешнего вида 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 вместо borderfill вместо 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;
}
Каждое деление состоит из линии и текста. Эти элементы можно стилизовать независимо, создавая визуальную иерархию.
.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;
}
Иногда необходимо полностью убрать визуальные части оси, сохранив структуру:
.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-оси чувствительны к рендерингу в браузере, поэтому используются дополнительные свойства:
.axis line,
.axis path {
shape-rendering: crispEdges;
}
Для более гладких линий:
.axis path.domain {
shape-rendering: geometricPrecision;
}
Выбор режима влияет на визуальную стабильность при масштабировании.
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 отвечает за общую визуальную систему.
При изменении размеров графика оси перерисовываются, но 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 подчиняются специфике каскада:
attr("fill"))Пример конфликта:
.attr("fill", "red")
.tick text {
fill: black;
}
В этом случае inline-значение имеет приоритет.
Для управления приоритетом используется:
.tick text {
fill: black !important;
}
В сложных визуализациях стили осей выносятся в отдельный слой абстракции:
Пример структуры:
.axis {
font-family: sans-serif;
}
.axis--compact .tick text {
font-size: 9px;
}
.axis--large .tick text {
font-size: 14px;
}
Такой подход позволяет масштабировать визуальную систему без изменения логики D3-кода.