Emmet: Custom Attributes `[attr=val]` and Numbering `$`

After the first 7 lessons, you can already write HTML with arbitrary nesting, arbitrary attributes, and arbitrary repetition using Emmet. But two "factory-style" scenarios remain unsolved—"10 inputs each with a different name" and "100 table rows with auto-incrementing numbers." These two scenarios are solved by custom attributes [attr=val] and numbering $ respectively—the final two puzzle pieces for Emmet's batch-generation capabilities.

1. What You Will Learn


2. A Frontend Developer's Real Story

(1) The Pain: Too Many Form Fields + Too Many Table Rows

Alice received a task to build a combined login + registration form with 5 inputs (username, email, password, confirm password, remember me) and 1 submit button. After writing one form, she realized that all the input names looked nearly identical—name="user", name="email", name="pwd", and so on—each one had to be typed manually.

Even worse was the "Terms of Service" table on the registration page: 100 rows of terms, each row a tr, with column 1 being a sequence number (1–100) and column 2 being the term text. Alice tried hand-writing: tr>td{1}+td{Term 1}, tr>td{2}+td{Term 2}, ... by row 30 she gave up. Just writing 30 similar "tr + 2 td" lines took her 30 minutes.

Finally, after writing all the fields, Alice also needed to add HTML5 attributes like aria-label="Username input" and data-validate="email" to the inputs—each attribute required checking specs, typing equals signs, and adding quotes. 30 input lines ballooned into 90 lines of HTML.

(2) How [attr=val] and $ Solve It

Emmet provides two complementary mechanisms to solve Alice's problems:

Custom attributes [attr=val]: Write any attribute inside square brackets, presented directly like HTML—Alice can write each input as input[name=user type=text] in one line, and aria and data-* attributes can similarly go inside the brackets.

Numbering $: Makes the class, id, and attribute values of repeated elements auto-increment. The 100 terms' sequence numbers can be generated with td.item$@100*100 in one line (starting from 100, descending), and the term text can be filled with {text} (covered in Lesson 09).

Alice rewrote the entire form in one line of Emmet:

TEXT 📖 Display only
form>(input[name=user type=text placeholder="Username"])+(input[type=email name=email])+(input[type=password name=pwd])+(input[type=password name=pwd-confirm])+(label>input[type=checkbox name=remember]+{Remember me})+(button[type=submit]{Login})

And the 100-row terms table:

TEXT 📖 Display only
(table>(tr>td{item $}*3)*5)

After verifying, Alice exclaimed: "5 input fields went from 25 hand-written lines to 1 line, and the 100-row terms table went from 350 lines to 1 line. Emmet's efficiency boost in form and table scenarios is 30x or more."

(3) Benefits

[attr=val] and $ are Emmet's core operators for handling "repetition + factory-style" scenarios. The former lets you cover all HTML5 attributes in one line of abbreviation, while the latter makes sequence numbers run automatically. After this lesson, Alice can write a 5-field form in 12 seconds and a 100-row table in 10 seconds. This lesson also marks the conclusion of Phase 2—the remaining 9 lessons will cover CSS shorthand and editor Actions.


3. Custom Attributes [attr=val]: HTML5 Attributes in Square Brackets

[attr=val] is Emmet's syntax for adding arbitrary attributes to a tag. Content written inside square brackets becomes raw HTML attributes—whether standard HTML5 attributes (colspan, type, placeholder), newer HTML5 attributes (data-*, aria-*), or custom attributes.

(1) Shorthand Rules

Abbreviation Expansion Notes
td[title="Hello"] <td title="Hello"></td> Single attribute with double quotes
td[colspan=3] <td colspan="3"></td> Single attribute without quotes (OK when value has no spaces)
td[title colspan=3] <td title="" colspan="3"></td> Abbreviated form (valueless attributes)
input[type=text name=user] <input type="text" name="user"> Multiple attributes separated by spaces
a[href="#" data-id=$] <a href="#" data-id="1"></a> data-* attribute with numbering placeholder

Tip: The [attr] valueless form (e.g., [disabled]) generates attr=""—the value is an empty string. If you want a boolean attribute (e.g., <input disabled>), Emmet won't automatically strip the value; you'll need to edit it manually. However, most modern HTML standard attributes support the value form, keeping things consistent.

Key Point: The content inside square brackets is parsed entirely according to HTML5 attribute syntax—values can use single quotes, double quotes, or no quotes (when there are no spaces); multiple attributes are separated by spaces. Note: here, spaces are valid delimiters, unlike Emmet's own "space = stop character" rule. Square brackets form a "closed" context.


4. Numbering $: The Auto-Incrementing Placeholder

$ is Emmet's numbering placeholder—write $ in a class, id, attribute value, or even text position, pair it with *N repetition, and it is automatically replaced with the current sequence number (default starting from 1).

(1) Where $ Can Be Used

Location Abbreviation Expansion (*3)
Inside class li.item$*3 <li class="item1">, <li class="item2">, <li class="item3">
Inside id div#user$*3 <div id="user1"> ...
Inside attribute value a[data-id=$]*3 <a data-id="1"> ...
Inside text ({}) a{Link $}*3 <a href="">Link 1</a> ... (covered in Lesson 09)

(2) Multi-Digit Zero-Padding $$$

Chaining multiple $ characters produces multi-digit zero-padding:

$ Count Expanded Form Use Case
item$ item1 item2 item3 Lists, menus
item$$ item01 item02 item03 Small lists
item$$$ item001 item002 item003 Table rows, cards
item$$$$ item0001 ... Rare (thousands of rows)

(3) Reverse $- and Starting Value $@N

By default, numbering starts from 1 ascending. Use @- for descending, and @N to start from N:

Modifier Meaning Example (*3)
$ (default) Ascending, from 1 1, 2, 3
$- or @- Descending (max → 1) 3, 2, 1
$@3 Ascending, from 3 3, 4, 5
$-3 or @-3 Descending, from 3 backwards 3, 2, 1

Key Point: In $@N, N is the starting value, not the ending value. This is commonly used for scenarios like "starting from a specific number" (e.g., pagination page numbers, product SKU codes, version numbers).


5. Practical Examples

The following 5 examples cover all core combinations of [attr] and $—from single attributes to multiple attributes, from basic numbering to multi-digit zero-padding, from reverse to table scenarios.

▶ Example: Basic Attributes td[title="Hello" colspan=3] (Difficulty ⭐)

Add both title and colspan attributes to a td. This is one of the most classic attribute combinations in HTML tables.

Abbreviation:

HTML
td[title="Hello" colspan=3]

Expansion:

HTML
<td title="Hello" colspan="3"></td>

Output:

TEXT 📖 Display only
Double quotes wrap the title containing a space ("Hello"), and no quotes wrap the spaceless value (3). Emmet automatically adds quotes to all attributes regardless of whether you included them in the abbreviation. Multiple attributes are separated by spaces, which is different from Emmet's own "space = stop character" rule—spaces have special meaning inside square brackets. colspan=3 indicates the cell spans 3 columns horizontally.

▶ Example: Input Attributes input[type=text name=user placeholder=Username] (Difficulty ⭐)

Add type, name, and placeholder attributes to an input in one go. Input is one of the most "attribute-dense" tags in HTML.

Abbreviation:

HTML
input[type=text name=user placeholder=Username]

Expansion:

HTML
<input type="text" name="user" placeholder="Username">

Output:

TEXT 📖 Display only
Three attributes completed in one abbreviation. Note: Emmet intelligently defaults input to type="text" (even if you omit type), so writing just `input[name=user placeholder=Username]` still produces type="text". If you want type="password" or type="email", you must write it explicitly. Three chained attributes are the highest-frequency shorthand in form scenarios.

▶ Example: Single $ Numbering ul>li.item$*5 (Difficulty ⭐)

Five li elements, each with a class that auto-increments from 1 to 5—the premier example from Emmet's official documentation.

Abbreviation:

HTML
ul>li.item$*5

Expansion:

HTML
<ul>
  <li class="item1"></li>
  <li class="item2"></li>
  <li class="item3"></li>
  <li class="item4"></li>
  <li class="item5"></li>
</ul>

Output:

TEXT 📖 Display only
Five li elements with classes item1, item2, item3, item4, item5. This is Emmet's standard pattern for uniquely numbered items (item + number). If the class name doesn't need a numeric part, simply write .item (without $), and all li elements share the same class.

▶ Example: Multi-Digit $$$ Zero-Padding ul>li.item$$$*5 (Difficulty ⭐)

Use 3 $ characters to auto-zero-pad numbering to 3 digits.

Abbreviation:

HTML
ul>li.item$$$*5

Expansion:

HTML
<ul>
  <li class="item001"></li>
  <li class="item002"></li>
  <li class="item003"></li>
  <li class="item004"></li>
  <li class="item005"></li>
</ul>

Output:

TEXT 📖 Display only
Zero-padding digit count = number of $ characters. 3 $ characters = 3-digit zero-padding (item001, item002...). Zero-padding scenarios include: order numbers (prefixed, easy to sort), table IDs, SKUs, and chapter numbers. For 4-digit padding, use item$$$$; for 5-digit, use item$$$$$. Note: when the actual number exceeds the padding width, it is not truncated—e.g., *12 with $$ produces item01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12 (the first 9 are zero-padded; numbers 10–12 display with their actual digits because they exceed 2 digits).

▶ Example: Table + Numbering (table>(tr>td{item $})*3) (Difficulty ⭐⭐)

Grouping + repetition + text content—a "numbered list" of 3 rows × 1 column, using grouping so that td follows each tr's repetition.

Abbreviation:

HTML
(table>(tr>td{item $})*3)

Expansion:

HTML
<table>
  <tr>
    <td>item 1</td>
  </tr>
  <tr>
    <td>item 2</td>
  </tr>
  <tr>
    <td>item 3</td>
  </tr>
</table>

Output:

TEXT 📖 Display only
Breakdown: the outer parentheses encapsulate the entire table as an independent unit; the inner (tr>td{item $}) is a template for "one row with one item N," and *3 repeats this template 3 times. Each repetition replaces $ with the current sequence number (1, 2, 3), resulting in 3 tr elements each containing one td with text "item 1," "item 2," and "item 3." This is Emmet's core pattern for scenarios like "terms numbering," "todo list numbering," and "product lists." To get multiple cells per row, change td{item $} to td{item $}+td{...} (text content covered in Lesson 09).

6. Complete Example: Form Fieldset + Multi-Digit Zero-Padded IDs

Combine [attr] and $—a registration form with 5 input fields, each field having a 3-digit zero-padded id.

▶ Example: Registration Form + 3-Digit IDs (Difficulty ⭐⭐)

Abbreviation:

HTML
form#register>(fieldset>legend{Account Info})+(input#field-001.form-control[type=text name=user])+(input#field-002.form-control[type=email name=email])+(input#field-003.form-control[type=password name=pwd])+(label>input#field-004[type=checkbox name=news]+{Subscribe to newsletter})+(button#btn-100.btn.btn-primary[type=submit]{Register})

Expansion:

HTML
<form id="register">
  <fieldset>
    <legend>Account Info</legend>
  </fieldset>
  <input id="field-001" class="form-control" type="text" name="user">
  <input id="field-002" class="form-control" type="email" name="email">
  <input id="field-003" class="form-control" type="password" name="pwd">
  <label>
    <input id="field-004" type="checkbox" name="news">
    Subscribe to newsletter
  </label>
  <button id="btn-100" class="btn btn-primary" type="submit">Register</button>
</form>

Output:

TEXT 📖 Display only
Breakdown: this example combines all Phase 2 operators—> (nesting), + (sibling), #id, .class, [] (attributes), {} (text content). Note: each input's id is hand-written because Emmet's $ can only be used inside *N repetition structures. Here, the 5 inputs use a hybrid approach—"manual id + auto class"—where id serves as the form's "field anchor" (JS hook) and must be unique, while class serves as the "field type" (CSS selector) and can be reused. This "hand-written id + auto class" pattern is a high-frequency pattern in form development.

Try It Yourself: Type td[title="Hello world!" colspan=3] into the Emmet Official Cheat Sheet to see the real-time expansion.


❓ FAQ

Q What is the difference between [attr] and [attr=]?
A [attr] is shorthand equivalent to [attr=""] (empty string value), expanding to <tag attr="">; [attr=] explicitly assigns an empty string, expanding to the same <tag attr="">. Both produce identical results. In HTML semantics, a valueless attribute (e.g., disabled="") behaves the same as a bare attribute name (disabled)—but Emmet defaults to generating the value form, so you may need to trim it manually.
Q Can [data-id=$] work without *?
A Yes, but without * repetition, $ defaults to 1. So a[data-id=$] expands to <a data-id="1"></a>—which is usually not what you want. If you just want a data ID without numbering, write a[data-id=user] with the actual value.
Q Which is correct, @- or $-?
A Emmet's official documentation supports both forms: @N means start from N, @- means descending. The combined form $-3 is equivalent to @-3—both mean descending starting from 3. Using the @ prefixed form ($@3, @3, @-3) is recommended for clarity and compatibility.

📖 Summary


📝 Exercises

  1. Basic (Difficulty ⭐): Write one-line Emmet abbreviations that expand to: (a) an a tag with href="#", class="link", data-id="$" (note: $ without * defaults to 1); (b) a td with title="Cell", colspan=2, rowspan=3; (c) a ul>li*5, each li with a class of row-$ (single-digit numbering).
  2. Intermediate (Difficulty ⭐⭐): Write a 5-row × 4-column "grade sheet" with the first row as a header (th) and rows 2–5 as data (td), with data cell ids using 3-digit zero-padding (cell-001 through cell-020). Write it all in one Emmet line and explain why $ can be used for ids but class wouldn't work the same way.
  3. Challenge (Difficulty ⭐⭐⭐): Write a 100-row "Terms of Service table" with a 3-column structure: (a) a sequence number column using td.item$@100*100 for reverse numbering (from 100 down to 1); (b) a title column using {Term $} text content (text content covered in Lesson 09); (c) a content column left empty for now. Write it all in one Emmet line, verify the expansion, and compare the time difference between hand-writing 100 rows vs. one line of Emmet.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏