- From: Christoph Päper via GitHub <noreply@w3.org>
- Date: Wed, 31 Dec 2025 00:16:33 +0000
- To: public-css-archive@w3.org
Crissov has just created a new issue for https://github.com/w3c/csswg-drafts:
== [css-border] `@border-style` for custom border designs ==
In https://github.com/w3c/csswg-drafts/issues/13044#issuecomment-3686993217 I proposed to solve the issue of multiple borders by introducing an at-rule to let authors specify **custom border styles**. @LeaVerou suggested this should be moved into a separate issue as it seemed useful but overkill for that particular use case. I’m not fully convinced about the latter, but here we go for the former.
The at-rule would be called `@border-style` or `@line-style`. It should require a (custom?) ident before its declaration block; possibly a comma-separated list thereof.
The `border-style`, `outline-style` and probably `text-decoration-style` properties would then need to accept a dashed ident as their value.
~~~~ css
@border-style --line {
fill: solid;
}
.foo {
border-style: --line;
outline-style: --line;
text-decoration-style: --line;
}
~~~~
The `fill` descriptor used herein would clash with the existing `fill` property (from SVG) – consider it a placeholder.
Color
-----
The new at-rule should optionally support multiple colors taken from the respective properties. In my first mockup code, I introduced a function `border-color()` for this, which took an integer index as an argument to refer to the respective listified `border-color` property value. Perhaps `nth-color(<an+b>)` would be better.
This is not the only solution imaginable and perhaps not the best. I figured that names _could_ be specified in the preamble, similar to custom functions:
~~~~ css
@border-style --tricolor (--top, --middle, --bottom) {
fill: stripe(
var(--bottom) 35%,
var(--middle) 33.5%,
var(--top) 32.5%
);
}
~~~~
If the value(s) of the respective ` border-width` property should also be available within the at-rule, the named parameters would need to be typed:
~~~~ css
@border-style --tricolor (
--top-color <color>, --middle-color <color>, --bottom-color <color>,
--top-width <length>, --middle-width <length>, --bottom-width <length>
) {
fill: stripes(
var(--bottom-color) var(--bottom-width),
var(--middle-color) var(--middle-width),
var(--top-color) var(--top-width)
);
}
~~~~
Alternatively, the at-rule could work with layers or segments, which each work with a single color in the order provided:
~~~~ css
@border-style --tricolor {
@layer 1 {
fill: /* first */ border-color;
}
@layer 2 {
fill: /* second */ border-color 67%, transparent;
}
@layer 3 {
fill: /* third */ border-color 33%, transparent;
}
}
~~~~
Shape
-----
All existing border styles or, more generally, [line styles](https://drafts.csswg.org/css-borders-4/#typedef-line-style) should be describable by this at-rule.
`solid` is the basic case without shapes, gaps or color manipulation. `none` and `hidden` are degenerate cases.
One group of predefined line styles – `dotted`, `dashed` and `wavy` – consists of a single basic shape that is repeated endlessly with some gap between repetitions. This gap might automatically be adjusted to avoid cutting off shapes at the corners (like spaces in justified text) and it may be zero.
Accordingly, the at-rule would need to describe a pattern of basic shapes and how it repeats. I see two approaches: Shapes are either filled themselves and populate the available empty space or they are masks/stencils that cut out parts of the solid fill.
The gap could be defined as padding-like part of the shape or it could be a separate entity; some dashed styles can already be achieved by [clipping](https://drafts.csswg.org/css-borders-4/#border-clip) solid borders instead.
~~~~ css
@border-style --dotted {
thickness: 102% /* overshoot a bit */;
fill: circle(100%);
}
~~~~
Elsewhere you can also find border styles with multiple shapes.
~~~~ css
@border-style --dash-dotted {
fill: rect(200% 80%) gap circle(100%);
}
~~~~
Depending on interpretation and implementation, `double` is related to this first group; it is either a solid line repeated once in the orthogonal direction of the shaped styles or it is a colon-like shape `:` repeated without any gap.
~~~~ css
@border-style --double-striped {
fill: rect(40%) gap rect(40%);
orientation: outwards; /* stripes stacked from inner to outer-most */
}
~~~~
Some epochs had very adorned border ornaments, which are possible only with external `border-image` yet, although `shape()` would suffice to describe many of them. It can also easily describe other popular shapes like stars and hearts. These may have a preferred orientation, e.g. relative to block progression and therefore not change for each edge.
~~~~ css
@border-style --starred {
fill: --star(5/* spokes */, 100%);
orientation: block; /* upwards-pointing pentagram stars */
}
@border-style --hearted {
fill: --heart(90%);
orientation: outwards;
}
~~~~
Sides
-----
Another group of predefined line styles – `groove`, `ridge`, `inset`, `outset` – derive two shades from the active color and use them differently for the four sides in order to achieve a 3D effect. (They _could_ be sensitive to the inline direction, but that is not specified.)
I’m not sure whether pseudo-classes as in `@page` would be a good solution for that.
~~~~ css
@border-style inset:top, inset:left, outset:bottom, outset:right {
fill: --darker();
}
~~~~
Alternatively, the descriptor could take a comma-separated list of values.
~~~~ css
@border-style inset {
fill: --darker(), --lighter(), --lighter(), --darker(),
}
~~~~
In the collapsing border model, `outset` and `inset` behave like `groove` and `ridge`, respectively. I’m not sure how to handle that.
~~~~ css
@border-style groove, outset:collapsing {
…
}
~~~~
~~~~ css
@border-style inset {
fill: if(style(border-collapse: collapse): …; …);
}
~~~~
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/13280 using your GitHub account
--
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config
Received on Wednesday, 31 December 2025 00:16:34 UTC