Emmet: Text Content `{text}` and Multi-`$` Zero-Padding
Through the first 8 lessons, you have learned Emmet's 8 HTML structure operators (
>+^*().class#id[attr]) and 1 numbering operator ($). But two "semantic" scenarios are still unsolved—"putting 'Click me' inside a button" and "10 cards with content numbered 001, 002." The former is solved by{text}text content insertion, and the latter by multi-$zero-padding. This lesson combines these two operators, upgrading your abbreviations from "building structure" to "generating content."
1. What You Will Learn
- Text content
{text}curly-brace syntax (including automatic special-character escaping) {text}adjacent to parent vs.>{text}nested: the difference between the two "position modes"- Combining numbering
$with{text}: each line of content gets a number (item 1, item 2, ...) - Multi-digit zero-padding
$$$$$$$$$: auto-generate 001, 002, 003 - Starting value
$@Nand reverse$@-: start numbering from a specified value, or count backwards
2. A Frontend Developer's Real Story
(1) The Pain: Tags Are Built, But Content Still Needs Typing Character by Character
Alice was building a "Features Showcase" section for a product landing page, requiring 6 cards, each containing an icon, title, description, and button. She first wrote the card structure using Lesson 06's () and implicit tags:
section.features>(.card*6>i.icon+h3+p+button.btn)
The structure was done in 1 line—Emmet expanded 6 cards, each with an icon, h3, p, and button. But then Alice had to write the content for each card: titles like "High Performance," "Ease of Use," "Scalable," "Secure," "Cross-Platform," "Open Source," a 30-character description for each, and the "Learn More" button text repeated 6 times.
Alice started by hand—expanded HTML with 30+ empty tags, filling in content one by one. Just the titles took 5 minutes, descriptions another 10, and finally she typed "Learn More" 6 times (or just copy-pasted it).
Even worse was the pagination: a 100-page product list with <li>Page 1</li> through <li>Page 100</li> at the bottom of each page. Alice gave up after 30 lines. She tried using $: (ul.pagination>li>a{Page $})*100—the expansion gave "Page 1" through "Page 100," but pages 1–9 came out as single-digit numbers, looking like a bug. She wanted "01," "02," ..., "99," "100"—the first 9 pages needed two-digit zero-padding, while the rest could display normally.
(2) How {text} and Multi-$ Zero-Padding Solve It
Emmet provides two complementary mechanisms:
Text content {text}: Content inside curly braces is inserted as a text node inside the tag. Alice's 6 cards could be written as:
section.features>(.card*6>(i.icon)+h3{High Performance}+p{This is the first description}+button.btn{Learn More})
But with 6 cards each needing different content, *6 can't simply repeat the same text—this is where looping templates with $ numbering come in.
Numbering $ and multi-digit zero-padding: $ is the current sequence number; $$ zero-pads to 2 digits; $$$ zero-pads to 3 digits. Alice's pagination can be written as:
(ul.pagination>li>a{Page $$})*100
Which expands to "Page 01," "Page 02," ..., "Page 99," "Page 100"—the first 9 are auto-zero-padded to 2 digits, and from page 10 onward they display normally.
Alice rewrote the entire Features section in one line:
section.features>(h2{Product Features})+((.card>(i.icon)+h3{Feature $}+p{This is feature $ of 6}+button.btn{Learn More})*6)
And the 100-line pagination:
(ul.pagination>li>a{Page $$})*100
Alice verified and exclaimed: "6 cards' content went from 30 hand-written lines to 1 line, and the 100-line pagination went from 200 lines to 1 line. Emmet's efficiency boost in batch content generation scenarios is 50x or more."
(3) Benefits
{text} and multi-$ zero-padding are Emmet's core for handling "content factory" scenarios. The former lets you write content directly in the abbreviation (no need to fill it in after expansion), while the latter makes repeated elements' numbers auto-run, zero-pad, start from, and count backwards. After this lesson, Alice can write 6 feature cards in 15 seconds and 100-page pagination in 10 seconds. This lesson also begins Phase 3—the next lesson covers Lorem Ipsum placeholder text and implicit tag details.
3. Text Content {text} Definition
{text} is Emmet's syntax for inserting a text node inside a tag. Content inside curly braces becomes the tag's inner text verbatim—it can include any characters (Chinese, spaces, punctuation, etc.).
(1) Shorthand Rules
| Abbreviation | Expansion | Notes |
|---|---|---|
a{Click me} |
<a href="">Click me</a> |
Single tag + single text block |
p{Hello world} |
<p>Hello world</p> |
p tag + text |
button{Submit} |
<button>Submit</button> |
Works in any language |
a{<strong>bold</strong>} |
<a href=""><strong>bold</strong></a> |
< and > auto-escaped to HTML entities |
Key Point: Special characters (
<>&) inside{text}are automatically escaped by Emmet to HTML entities (<>&). This is an HTML5 safety requirement—preventing user input with angle brackets from breaking document structure. If you want to insert real HTML tags (so<strong>is parsed as a tag rather than text), you'll need the "filters" covered in Lesson 13, or manually edit after expansion.
(2) Where {text} Can Be Placed Inside a Tag
| Position | Abbreviation | Expansion |
|---|---|---|
| Adjacent to tag name | a{Click me} |
<a href="">Click me</a> |
With > nesting |
p>{Click }+a{here} |
<p>Click <a href="">here</a></p> |
Bare {text} |
{ to continue} |
to continue (bare text, as an independent element) |
With $ numbering |
li{item $}*3 |
<li>item 1</li>, <li>item 2</li>, <li>item 3</li> |
4. Two Positions for {text}: Adjacent to Parent vs. Nested
This is the most refined design in Emmet's text content system. When {text} immediately follows a tag name, it does not change the parent context; but >{text} pushes the context one level deeper. The difference is the most common pitfall for beginners.
(1) Adjacent Mode: a{click}+b{here}
When {text} immediately follows a tag name (without a > prefix), the text is treated as a direct child of that tag, but it does not change the parent context—meaning subsequent + sibling elements remain at the same level as the current tag.
a{click}+b{here}
<a href="">click</a><b>here</b>
(2) Nested Mode: a>{click}+b{here}
When {text} is preceded by >, Emmet treats the >{text} as a child element and pushes the parent context inside a—subsequent + sibling elements are appended inside a.
a>{click}+b{here}
<a href="">click<b>here</b></a>
(3) Multi-Element Mixed Text: p{Click }+a{here}+{ to continue}
This classic example from the official documentation demonstrates the difference between the two modes:
p{Click }+a{here}+{ to continue}
<p>Click </p>
<a href="">here</a> to continue
p>{Click }+a{here}+{ to continue}
<p>Click <a href="">here</a> to continue</p>
Key Point: The first abbreviation (adjacent mode) produces 3 root-level elements (p, a, bare text), while the second (nested mode) puts the entire "Click here to continue" inside
<p>. The difference comes from>: in the first version,{Click }makes p into a "p with click text" (without drilling down), while the second version's>{Click }pushes the context from p into p's interior, so the subsequent+a{here}and+{ to continue}are both appended inside p.
▶ Example: Basic Text Content a{Click me} (Difficulty ⭐)
The first text content example from Emmet's official documentation—adding a block of text to an a tag.
Abbreviation:
a{Click me}
Expansion:
<a href="">Click me</a>
Output:
TEXT 📖 Display onlyBreakdown: a is the tag name (which defaults to including href=""), and {Click me} is the text node. Emmet automatically adds the href="" attribute to a (HTML5 default behavior), and the text "Click me" becomes a's child node. Note: {text} after the a tag does not change the parent context—if followed by +b{some}, b will be a sibling of a, not a child of a.
▶ Example: Multi-Element Mixed Text p>{Click }+a{here}+{ to continue} (Difficulty ⭐⭐)
Use nested mode to put the entire "Click here to continue" inside <p>.
Abbreviation:
p>{Click }+a{here}+{ to continue}
Expansion:
<p>Click <a href="">here</a> to continue</p>
Output:
TEXT 📖 Display onlyBreakdown: p>{Click } pushes the context inside p, and "Click " becomes p's first text child; +a{here} appends an a with "here" text inside p; +{ to continue} appends the bare text " to continue" inside p. The final p contains 3 children: text "Click ", an a tag, and text " to continue". Without > (i.e., p{Click }+a{here}+{ to continue}), p would only contain "Click ", and a and the bare text would become root-level siblings—resulting in <p>Click </p> followed by <a>here</a> and bare text.
5. Numbering $ and Multi-Digit Zero-Padding
$ is Emmet's numbering placeholder (introduced in Lesson 08). This lesson extends it with two advanced uses: multi-digit zero-padding and start/reverse control.
(1) Multi-Digit Zero-Padding $$ $$$ $$$$
Lesson 08 used a single $ to generate 1, 2, 3. When the number range exceeds 9 (e.g., 100 table row numbers), a single $ displays mixed digit widths: 1, 2, ..., 9, 10, 11. Multiple $ characters make numbering auto-zero-pad to a fixed minimum width:
$ Count |
Expanded Form (*5) |
Use Case |
|---|---|---|
$ |
1, 2, 3, 4, 5 | Simple lists |
$$ |
01, 02, 03, 04, 05 | Small lists, months |
$$$ |
001, 002, 003, 004, 005 | Order numbers, table row IDs |
$$$$ |
0001, 0002, ... | Thousand-row tables, chapter numbers |
Key Point: The number of
$characters determines the minimum padding width. If the actual number exceeds this width, Emmet does not truncate—*12with$$expands to01, 02, ..., 09, 10, 11, 12(the first 9 are zero-padded; the last 3 display normally). This semantic is "padding width = minimum width, not maximum."
(2) Starting Value $@N and Reverse $@-
By default, numbering starts from 1 ascending. Use @N to start from N, and @- for descending:
| Modifier | Meaning | Example (*5) |
|---|---|---|
$ (default) |
Ascending, from 1 | 1, 2, 3, 4, 5 |
$@3 |
Ascending, from 3 | 3, 4, 5, 6, 7 |
$@- |
Descending (iteration count → 1) | 5, 4, 3, 2, 1 |
$@-3 |
Descending, from 3 backward | *5 → 7, 6, 5, 4, 3 (based on @-3 start + iteration count overlay) |
Note:
$@-3is supported in Emmet 1.2+. Common combinations are$@N(starting value) and$@-(descending starting from the iteration count). In@N, N is the starting value, not the ending value.
▶ Example: Numbering + Text ul>li{item $}*3 (Difficulty ⭐)
Embed $ inside {text} so each li's content carries an auto-number.
Abbreviation:
ul>li{item $}*3
Expansion:
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
Output:
TEXT 📖 Display onlyBreakdown: li{item $} is "an li tag + a block of text (item + current number)," and *3 repeats this template 3 times. Each repetition replaces $ with the current iteration number (1, 2, 3). The final 3 li elements each contain a block of "item N" text. This is Emmet's core pattern for scenarios like todo lists, feature lists, and FAQ numbering. Note: the space after "item" is part of the text (not an operator).
▶ Example: Zero-Padding ul>li{$$}*5 (Difficulty ⭐)
2-digit zero-padding—the first 9 elements auto-zero-pad; numbers beyond 9 display normally.
Abbreviation:
ul>li{$$}*5
Expansion:
<ul>
<li>01</li>
<li>02</li>
<li>03</li>
<li>04</li>
<li>05</li>
</ul>
Output:
TEXT 📖 Display onlyBreakdown: $$ means "display the current number with 2 digits." *5 repeats li 5 times, with $ values of 1, 2, 3, 4, 5—after 2-digit zero-padding, they become 01, 02, 03, 04, 05. With *12, the expansion would be 01, 02, ..., 09, 10, 11, 12—the first 9 are zero-padded, while the last 3 exceed 2 digits and display normally (no truncation). Zero-padding scenarios: pagination page numbers, months, product SKU codes.
▶ Example: Starting Value + Reverse ol>li{Step $@3}*5 (Difficulty ⭐⭐)
A 5-step process starting from 3—use $@3 to control the starting value.
Abbreviation:
ol>li{Step $@3}*5
Expansion:
<ol>
<li>Step 3</li>
<li>Step 4</li>
<li>Step 5</li>
<li>Step 6</li>
<li>Step 7</li>
</ol>
Output:
TEXT 📖 Display onlyBreakdown: $@3 means "the current number starts from 3." ol>li makes li an ordered child of ol; {Step $@3} is the text template where "Step " is a fixed string and $@3 is the current number starting from 3. *5 repeats li 5 times, with numbers 3, 4, 5, 6, 7. This is Emmet's standard pattern for scenarios like "pagination starting from a certain page," "product chapters starting from chapter 3," and "step numbers not starting from 1." For reverse (from 5 down to 1), use $@- instead (auto-backtracks based on the iteration count).
6. Complete Example: Paginator + Numbering + Text Content
Combine {text}, multi-$ zero-padding, and $@N starting value—a 100-page paginator with each page displayed in 2-digit zero-padded format.
▶ Example: 100-Page Paginator (Difficulty ⭐⭐)
Abbreviation:
(ul.pagination>li.page$$>a{Page $$})*100
Expansion (excerpt):
<ul class="pagination">
<li class="page01"><a href="">Page 01</a></li>
<li class="page02"><a href="">Page 02</a></li>
<li class="page03"><a href="">Page 03</a></li>
<!-- ... -->
<li class="page09"><a href="">Page 09</a></li>
<li class="page10"><a href="">Page 10</a></li>
<li class="page11"><a href="">Page 11</a></li>
<!-- ... -->
<li class="page99"><a href="">Page 99</a></li>
<li class="page100"><a href="">Page 100</a></li>
</ul>
Output:
TEXT 📖 Display onlyBreakdown: this example combines all of this lesson's operators—{} (text content), $ (numbering), $$ (2-digit zero-padding), () (grouping), * (multiplication), .class, > (nesting). The outer parentheses make the entire li template a repeatable unit; li.page$$ uses $$ in both the class name and the {text}, ensuring the class and content numbers stay in sync; the a tag acts as the li's child with content "Page XX." Note: the actual HTML for 100 pages has 100 li elements; this excerpt shows only the first 3, 9–11, and 99–100. Padding width = 2: pages 1–9 are 01–09, and from page 10 onward the numbers exceed 2 digits and display normally (10, 11, ...). For 3-digit zero-padding (001–100), use $$$ instead.
❓ FAQ
< > inside {text}?< becomes <, > becomes >, and & becomes &. This is an HTML5 safety requirement to prevent user input from breaking document structure. So a{<strong>bold</strong>} expands to <a href=""><strong>bold</strong></a> (angle brackets become entity characters; the browser displays the literal text "<strong>bold</strong>" without parsing it as a tag). To insert real HTML tags, use filters or manually edit after expansion.a{click}+b{here} and a>{click}+b{here} after expansion?<a>click</a><b>here</b>. The latter (nested mode) embeds <b> inside <a>: <a>click<b>here</b></a>. The critical difference is the >: adjacent mode does not change the parent context; nested mode pushes the context one level deeper. The mnemonic is: "if there's a > before the curly brace, go one level deeper; if not, stay at the current level." This is the most refined design in Emmet's text content system and a boundary every beginner must master.$ appear multiple times within the same {text} block? Do multiple $ symbols produce the same or different numbers?$ symbols within the same {text} block share the same number (the current iteration count). For example, li{item $: number $}*3 expands to <li>item 1: number 1</li>, <li>item 2: number 2</li>, <li>item 3: number 3</li>. Multiple $ are mainly used for padding-width control ($ for raw number, $$ for 2-digit zero-padding, $$$ for 3-digit zero-padding). To have different $ symbols within the same element produce different numbers (e.g., i=1, j=2), you need nested * repetition structures.$@N be a negative number? What does N=0 mean?$@-5*3 means ascending from -5 (-5, -4, -3); $@0*3 means ascending from 0 (0, 1, 2). However, negative numbers and 0 are rarely used as starting values in practice (negative sequence numbers are meaningless in HTML, and starting from 0 skips 1). Common usage: $@1 explicitly starts from 1 (equivalent to the default but more explicit in code), and $@100 starts from 100 (deep pagination scenarios). Use $@- for reverse; don't mix $@-N unless necessary.📖 Summary
{text}is the text content syntax; content inside curly braces becomes the tag's text node (e.g.,li{item $}*3→ "item 1," "item 2," "item 3")<>&inside{text}are auto-escaped to HTML entities (<>&)- Adjacent mode
a{text}does not change parent context; nested modea>{text}pushes context one level deeper $is the numbering placeholder; multiple$auto-zero-pad:$$= 2 digits,$$$= 3 digits,$$$$= 4 digits (padding width = minimum width; no truncation when exceeded)$@Nstarts numbering from N;$@-produces descending numbering (based on iteration count)
📝 Exercises
- Basic (Difficulty ⭐): Write one-line Emmet abbreviations that expand to: (a) a
button{Sign Up}tag; (b) a<strong>tag with text "Warning"; (c) ap>loremparagraph (i.e.,<p>Lorem ipsum...</p>, not covered in this lesson; Lorem is covered in the next lesson). - Intermediate (Difficulty ⭐⭐): Write a 12-item "Month List"
ol>li{Month $$}*12and verify the expansion (should be "Month 01" through "Month 12"). Then rewrite as a "Quarter List"ol>li{Quarter $}*4, verifying numbering from 1–4. Finally, write a "Product Features Section" with 5 cards, each containing<h3>Feature $</h3>and<p>Description for feature $</p>, all in one Emmet line. - Challenge (Difficulty ⭐⭐⭐): Write a 100-page paginator with 3-digit zero-padding (
Page 001throughPage 100). Requirements: (a) class usingpage$$$(3-digit zero-padding); (b) text usingPage $$$(3-digit zero-padding); (c) starting page using$@1(same as default behavior but explicitly declared). Verify the expansion in one Emmet line, and explain why$$$does not truncate numbers beyond 99 (hint: Emmet's zero-padding = minimum width, not maximum width).