Home > Software design >  how to set :before horizontal sizing fixed?
how to set :before horizontal sizing fixed?

Time:11-09

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>

JS Fiddle demo.

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>

JS Fiddle demo.

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>

JS Fiddle demo.

References:

  • Related