I'm trying to make ul menu with :before pseudoelement on li tags. Text inside :before's content is a different text for different rows. I want make this text right-aligned for each li, like this:
pseudo_x li
pseudo_xx li
pseudo_xxx li
li:nth-child(n):before {
content: "===";
border: 1px dotted red;
}
li:nth-child(2n):before {
content: "-----------";
border: 1px dotted red;
}
<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
<li>Fourth</li>
</ul>
*** Update
Thanks for answers. But I can't use list-style-type because I will format the :before's content. Also I can't use one width for all contents. Because the idea I writing all this, is to use content for display count of rows inside li tags, like this:
var rows = document.getElementsByTagName('li');
for(var i=0;i<rows.length;i )
rows[i].setAttribute("c", "rows: " "1".repeat(Math.random()*10|0))
li{
list-style-type: none;
}
li:before{
color: white;
content: attr(c);
}
.red:before{background:red}
.orange:before{background:orange}
.blue:before{background:blue}
.green:before{background:green}
<ul>
<li >First</li>
<li >Second</li>
<li >Third</li>
<li >Fourth</li>
</ul>
CodePudding user response:
You can set the list-style-type
, but then I don't think you can add a border to it
li:nth-child(n) {
list-style-type: "===";
}
li:nth-child(2n) {
list-style-type: "-----------";
}
<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
<li>Fourth</li>
</ul>
CodePudding user response:
One way is to make use of the CSS @counter-style
at-rule:
/* custom properties for styling the gutter,
this allows ancestor elements access to
the sizing of their descendant (since
the properties are available outside of
those descendants): */
:root {
/* the spacing between the generated content
counter and the <li> itself: */
--counterGutter: 0.5em;
/* the width/inline-size of the counter
pseudo-element: */
--counterWidth: 4em;
}
/* defining the counter to use, and assigning a
name to that counter (here: 'lines'): */
@counter-style lines {
/* causes the counter to cycle through the
provided symbols: */
system: cyclic;
/* white-space separated list of symbols that
the list counter should cycle through: */
symbols: "===" "-----";
}
ul {
/* resetting the counter for the <li>: */
counter-reset: liCounter;
/* removing the default counter: */
list-style-type: none;
}
li {
/* using logical properties (see the references)
to set a margin of 0.5em before and after the
<li> elements on the block axis: */
margin-block: 0.5em;
/* setting a margin on the inline axis equal to
the inline-size of the counter: */
margin-inline-start: var(--counterWidth);
/* to allow the pseudo-element child to be
positioned relative to this ancestor: */
position: relative;
}
li::before {
border: 1px dotted #f00;
/* using the counter() function to define the
counter, and counter-style, to use: */
content: counter(liCounter, lines);
/* incrementing the relevant counter: */
counter-increment: liCounter;
/* setting the inline-size (logical property,
see references) to the defined custom-
property: */
inline-size: var(--counterWidth);
position: absolute;
/* aligning the text using - again - logical
properties, this is equal to right-aligned
in the English (and Latin) language(s): */
text-align: end;
/* moving the element by the size of the defined
--counterGutter and its own width (100%), using
-1 to move the element before the content of
its ancestor on the inline axis: */
translate: calc(-1 * (var(--counterGutter) 100%));
}
<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
<li>Fourth</li>
</ul>
Following the comment (below) from OP:
…do you know how to save the original width of [
::before
] content?
There are a number of options to do this, which allows the inherent width of the counter glyphs to show, all of which have problems that may make them untenable in practice.
The first is my preference – but requires the use of subgrid
, which is currently supported only in Safari and Firefox:
@counter-style lines {
system: cyclic;
symbols: "===" "-----";
suffix: " ";
}
ul {
counter-reset: liCounter;
/* setting a display grid on the <ul> */
display: grid;
/* setting 0.5em gap between adjacent cells, on
both row and column axes: */
gap: 0.5em;
/* defining two columns, the first of which is as
small as possible while fitting the content and
the second of which takes one fraction of the
remaining space available: */
grid-template-columns: min-content 1fr;
list-style-type: none;
}
li {
/* using display: grid on the child <li> elements
as well: */
display: grid;
/* placing the <li> element by having it start in
the first column and to span two columns of the
parent element: */
grid-column: 1 / span 2;
/* setting the columns of the parent <ul> as the
columns of the <li> grid: */
grid-template-columns: subgrid;
}
li::before {
border: 1px dotted #f00;
content: counter(liCounter, lines);
counter-increment: liCounter;
/* justifying the content to the end
of the inline axis, so the border
wraps only the content, not the
whole cell: */
justify-self: end;
}
<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
<li>Fourth</li>
</ul>
Another option is to simply omit the sizing properties of the pseudo-element as used in the first code; unfortunately there's no obvious way to have access to the inherent sizes of the pseudo-elements. Without access to the size, the layout is a little fragile and may break at certain points:
:root {
--counterGutter: 0.5em;
}
@counter-style lines {
system: cyclic;
symbols: "===" "-----";
suffix: " ";
}
ul {
counter-reset: liCounter;
list-style-type: none;
}
li {
margin-block: 0.5em;
/* a dimension that's probably big
enough to fit the pseudo-element
inside, without being clipped
by overflow rules or being placed
over the <li> content: */
margin-inline-start: 5em;
position: relative;
}
li::before {
border: 1px dotted #f00;
content: counter(liCounter, lines);
counter-increment: liCounter;
position: absolute;
text-align: end;
translate: calc(-1 * (var(--counterGutter) 100%));
}
<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
<li>Fourth</li>
</ul>
References: