HTML Guides for span
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The HTML specification defines a specific set of allowed attributes for each element. The <span> element supports global attributes (such as id, class, style, title, etc.) but does not recognize currency as a valid attribute. When you add a non-standard attribute like currency to an element, the W3C validator flags it because it doesn't conform to the HTML standard.
This pattern often appears in e-commerce sites, financial applications, or internationalization contexts where developers want to associate a currency code (like USD, EUR, or GBP) with a price displayed in a <span>. While browsers will typically ignore unrecognized attributes without breaking the page, using them creates several problems:
- Standards compliance: Invalid HTML can lead to unpredictable behavior across different browsers and future browser versions.
- Maintainability: Other developers (or tools) won't recognize non-standard attributes, making the codebase harder to understand and maintain.
- Accessibility: Assistive technologies rely on valid, well-structured HTML. Non-standard attributes may be ignored or misinterpreted.
- JavaScript interoperability: The
HTMLElement.datasetAPI is specifically designed to work withdata-*attributes, providing a clean and standard way to read custom data from elements.
The fix is straightforward: HTML provides the data-* attribute mechanism specifically for embedding custom data on elements. Any attribute prefixed with data- is valid on any HTML element, and its value is accessible in JavaScript via the element.dataset property.
Examples
❌ Invalid: Non-standard currency attribute
<spancurrency="USD">49.99</span>
This triggers the validation error because currency is not a recognized attribute for <span>.
✅ Fixed: Using a data-currency attribute
<spandata-currency="USD">49.99</span>
The data-currency attribute is valid HTML. In JavaScript, you can access its value like this:
constspan=document.querySelector('span');
console.log(span.dataset.currency);// "USD"
✅ Alternative: Using data-* with richer markup
If you need to convey more structured information, you can combine multiple data-* attributes:
<spanclass="price"data-currency="EUR"data-amount="29.99">€29.99</span>
✅ Alternative: Using microdata or structured markup
For SEO and machine-readable data, consider using established vocabularies like Schema.org:
<spanitemscopeitemtype="https://schema.org/MonetaryAmount">
<metaitemprop="currency"content="USD">
<spanitemprop="value">49.99</span>
</span>
This approach provides semantic meaning that search engines and other consumers can understand, while remaining fully valid HTML.
The HTML specification defines a specific set of global attributes (like class, id, title, style, etc.) that are allowed on all elements, plus element-specific attributes for certain tags. Any attribute that isn't part of these standard sets and doesn't follow the data-* custom attribute convention is considered invalid and will trigger a validation error.
This issue is especially common with older integrations of third-party tools like ShareThis, AddThis, or similar social sharing widgets, particularly when embedded through a CMS like Drupal or WordPress. These older implementations relied on proprietary attributes such as displayText, st_url, and st_title directly on <span> or other elements. Modern versions of these tools have since migrated to valid data-* attributes.
Why This Matters
- Standards compliance: Non-standard attributes violate the HTML specification, meaning your markup is technically invalid and may behave unpredictably across browsers.
- Forward compatibility: Browsers could introduce a native
displaytextattribute in the future with entirely different behavior, causing conflicts with your code. - Accessibility: Assistive technologies rely on well-formed HTML. Non-standard attributes can confuse screen readers or other tools that parse the DOM.
- Maintainability: Using the standardized
data-*convention makes it immediately clear to other developers that an attribute holds custom data, improving code readability.
How to Fix It
- Identify all non-standard attributes on your elements (e.g.,
displayText,st_url,st_title). - Prefix each one with
data-to convert it into a valid custom data attribute (e.g.,data-displaytext,data-st-url,data-st-title). - Update any JavaScript that references these attributes. If JavaScript accesses them via
element.getAttribute('displayText'), change it toelement.getAttribute('data-displaytext')or use thedatasetAPI (element.dataset.displaytext). - If using a CMS or third-party plugin, update the plugin or module to its latest version, which likely uses valid
data-*attributes already.
Note that data-* attribute names should be all lowercase. Even if the original attribute used camelCase like displayText, the corrected version should be data-displaytext.
Examples
Invalid: Non-standard attribute on a <span>
<spanclass="st_sharethis"displaytext="ShareThis"st_url="https://example.com"st_title="My Page">
Share
</span>
This triggers validation errors for displaytext, st_url, and st_title because none of these are valid HTML attributes.
Valid: Using data-* attributes
<spanclass="st_sharethis"data-displaytext="ShareThis"data-st-url="https://example.com"data-st-title="My Page">
Share
</span>
Accessing data-* attributes in JavaScript
If your JavaScript relied on the old attribute names, update the references:
<spanid="share-btn"data-displaytext="ShareThis">Share</span>
<script>
constbtn=document.getElementById('share-btn');
// Using getAttribute
consttext=btn.getAttribute('data-displaytext');
// Using the dataset API
consttextAlt=btn.dataset.displaytext;
</script>
Valid: Using a standard attribute instead
In some cases, the intent of displaytext is simply to provide a label or tooltip. If so, a standard attribute like title may be more appropriate:
<spanclass="st_sharethis"title="ShareThis">Share</span>
Choose the approach that best matches your use case — data-* for custom data consumed by JavaScript, or a semantic HTML attribute if one already serves the purpose.
HTML has a defined set of global attributes (such as id, class, lang, and title) and element-specific attributes. Any attribute that isn't part of these recognized sets will trigger a validation error. The st_title attribute is a proprietary, non-standard attribute that was used by older versions of the ShareThis sharing widget to pass metadata — specifically, the title of the content being shared.
The HTML5 specification introduced data-* attributes as the standard mechanism for embedding custom data on elements. These attributes allow developers to store arbitrary information without conflicting with the HTML spec. Newer versions of ShareThis and similar services have adopted this convention, but legacy code — especially in CMS themes, plugins, or modules — may still use the old non-standard format.
Using invalid attributes causes several problems:
- Standards compliance: The document fails W3C validation, which can indicate deeper markup quality issues.
- Future compatibility: Browsers are not required to handle non-standard attributes in any predictable way. Future browser updates could ignore or strip them.
- Maintainability: Non-standard attributes make code harder for other developers to understand and maintain.
- Accessibility tools: Screen readers and other assistive technologies rely on well-formed HTML. Invalid attributes can cause unexpected behavior in these tools.
To fix this, replace all proprietary ShareThis attributes with their data-* equivalents. For example, st_title becomes data-st-title, st_url becomes data-st-url, and displayText becomes data-st-displaytext. You should also update the ShareThis JavaScript library to a version that recognizes the new attribute format.
Examples
❌ Invalid: Using proprietary attributes
<spanclass="st_sharethis"st_title="My Article"st_url="https://example.com/article"displayText="ShareThis">
Share
</span>
This triggers validation errors for st_title, st_url, and displayText because none of these are valid HTML attributes.
✅ Valid: Using data-* attributes
<spanclass="st_sharethis"data-st-title="My Article"data-st-url="https://example.com/article"data-st-displaytext="ShareThis">
Share
</span>
All custom data is now stored in properly namespaced data-* attributes, which are fully compliant with the HTML5 specification.
✅ Valid: Using a button element with data-* attributes
If the element is interactive (e.g., it triggers a share action on click), consider using a <button> instead of a <span> for better accessibility:
<buttontype="button"class="st_sharethis"data-st-title="My Article"data-st-url="https://example.com/article">
Share this article
</button>
Fixing this in a CMS
If you're using Drupal, WordPress, or another CMS with a ShareThis module or plugin:
- Update the plugin/module to the latest version — most have already migrated to
data-*attributes. - Check your theme templates for hardcoded ShareThis markup that may still use the old attribute format.
- Search your codebase for
st_title,st_url, anddisplayTextand replace them withdata-st-title,data-st-url, anddata-st-displaytextrespectively. - Update the ShareThis JavaScript to a version compatible with the new attribute names, and verify that sharing functionality still works after the change.
HTML has a defined set of global attributes (like class, id, title, lang, etc.) and element-specific attributes that are permitted by the specification. Any attribute that falls outside this set — such as st_url, st_title, or displayText — will trigger a validation error because the browser and the validator don't recognize it as part of the HTML standard.
The st_url attribute, along with related attributes like st_title, st_via, and displayText, originated from early versions of the ShareThis social sharing library. These were proprietary attributes that the ShareThis JavaScript would read from the DOM to configure sharing behavior. While browsers generally ignore attributes they don't understand (so the page may still appear to work), using non-standard attributes violates the HTML specification and can cause several problems:
- Standards compliance: Invalid HTML can lead to unpredictable behavior across different browsers and future browser versions.
- Accessibility: Screen readers and other assistive technologies may not handle non-standard attributes correctly, potentially causing confusion.
- Maintainability: Non-standard markup is harder for other developers to understand and maintain.
- SEO: Search engine crawlers may penalize or misinterpret pages with significant validation errors.
HTML5 introduced data-* attributes specifically to solve this problem. Any custom data you need to attach to an element can use this pattern — for example, data-st-url instead of st_url. The ShareThis library itself updated its integration to use data-* attributes in later versions, so modern implementations should already follow this pattern.
How to Fix
- Replace proprietary attributes with
data-*equivalents: Convertst_urltodata-st-url,st_titletodata-st-title,displayTexttodata-display-text, and so on. - Update your ShareThis integration: If you're using an outdated version of ShareThis (or an outdated CMS module/plugin like an old Drupal integration), update to the latest version, which uses valid HTML5 attributes.
- Check your CMS templates: If the invalid attributes are hardcoded in a theme template or a content block, update them manually.
Examples
Invalid: Proprietary attributes on a <span>
<spanclass="st_facebook_large"displayText="Facebook"st_url="https://example.com"st_title="My Page Title"></span>
<spanclass="st_twitter_large"displayText="Twitter"st_url="https://example.com"st_title="My Page Title"></span>
This markup uses st_url, st_title, and displayText, none of which are valid HTML attributes.
Valid: Using data-* attributes instead
<spanclass="st_facebook_large"data-display-text="Facebook"data-st-url="https://example.com"data-st-title="My Page Title"></span>
<spanclass="st_twitter_large"data-display-text="Twitter"data-st-url="https://example.com"data-st-title="My Page Title"></span>
By prefixing each custom attribute with data-, the markup becomes valid HTML5. Note that you may also need to update the associated JavaScript to read from these new attribute names (e.g., using element.dataset.stUrl instead of element.getAttribute('st_url')).
Valid: Modern ShareThis integration
If you're updating your ShareThis integration entirely, the modern approach uses a different markup pattern:
<divclass="sharethis-inline-share-buttons"data-url="https://example.com"data-title="My Page Title"></div>
Modern versions of the ShareThis library expect data-* attributes by default, so upgrading the library and its associated markup is the cleanest solution. Check the ShareThis documentation for the latest recommended integration approach for your platform.
The dir attribute cannot be an empty string — it must be one of the allowed values: ltr, rtl, or auto.
The dir attribute specifies the text directionality of an element's content. It is a global attribute, meaning it can be used on any HTML element. When you set dir="", the validator rejects it because an empty string doesn't convey any meaningful direction.
If you don't need to specify a direction, simply remove the dir attribute entirely. The element will naturally inherit the directionality from its parent. If you want the browser to determine the direction based on the text content, use dir="auto".
Invalid Example
<spandir="">Some text</span>
Valid Examples
<!-- Remove the attribute to inherit direction from the parent -->
<span>Some text</span>
<!-- Or explicitly set a valid direction -->
<spandir="ltr">Some text</span>
<!-- Or let the browser decide based on content -->
<spandir="auto">Some text</span>
The aria-expanded attribute requires the element to have an appropriate role attribute (or be an element that natively implies one). A <span> is a generic inline element with no implicit ARIA role, so you must explicitly assign a role when using ARIA state attributes like aria-expanded.
The aria-expanded attribute indicates whether a grouping element controlled by this element is currently expanded or collapsed. It is only valid on elements with specific roles such as button, link, combobox, menuitem, or other widget roles. When a <span> uses aria-expanded without a role, validators flag it because there's no semantic context for that state.
Since this element toggles a dropdown menu and has aria-label, aria-controls, and aria-expanded, the most appropriate role is button. This tells assistive technologies that the element is interactive and can be activated.
Also note that when using role="button" on a non-interactive element like <span>, you should ensure it is focusable by adding tabindex="0" and that it handles keyboard events (Enter and Space keys).
HTML Examples
❌ Invalid: aria-expanded on a span without a role
<spanclass="navbar-dropdown-icon"
aria-expanded="false"
aria-label="List options"
aria-controls="dropdown-menu-item-1-1menu-item-2-6"
data-toggle="dropdown">
</span>
✅ Valid: adding role="button" and tabindex="0"
<spanclass="navbar-dropdown-icon"
role="button"
tabindex="0"
aria-expanded="false"
aria-label="List options"
aria-controls="dropdown-menu-item-1-1menu-item-2-6"
data-toggle="dropdown">
</span>
✅ Better: use a <button> element instead
<buttonclass="navbar-dropdown-icon"
type="button"
aria-expanded="false"
aria-label="List options"
aria-controls="dropdown-menu-item-1-1menu-item-2-6"
data-toggle="dropdown">
</button>
Using a native <button> is preferred because it is focusable and keyboard-accessible by default, without needing role or tabindex.
The span element is a generic inline container with no inherent semantics. On its own, it carries no meaning for assistive technologies. When you add ARIA attributes like aria-expanded or aria-valuenow to a span, you are signaling that the element represents an interactive widget — but the validator (and assistive technologies) need more context. Many ARIA attributes are only permitted on elements that have certain roles, and some roles require a specific set of attributes to function correctly.
For example, aria-valuenow is designed for range widgets like sliders and progress bars. According to the WAI-ARIA specification, if you use aria-valuenow, the element must also have aria-valuemin, aria-valuemax, and a role such as progressbar, slider, meter, or scrollbar. Similarly, aria-expanded is meant for elements with roles like button, combobox, link, or treeitem. Placing these attributes on a bare span without the corresponding role violates the ARIA rules and triggers this validation error.
This matters for several reasons:
- Accessibility: Screen readers rely on the
roleto determine how to present a widget to users. Without it, ARIA state attributes become meaningless or confusing. - Standards compliance: The HTML specification integrates ARIA rules, and validators enforce that ARIA attributes are used in valid combinations.
- Browser behavior: Browsers use the
roleto build the accessibility tree. Aspanwitharia-valuenowbut norolemay be ignored or misrepresented to assistive technology users.
How to fix it
- Add the correct
roleto thespan, along with all attributes required by that role. - Use a semantic HTML element instead of a
spanwhen one exists (e.g.,<progress>or<button>). - Remove unnecessary ARIA attributes if the
spanis purely decorative or the attributes were added by mistake.
If your span is purely visual (e.g., a decorative asterisk for required fields), don't add state-related ARIA attributes to it. Instead, use aria-hidden="true" to hide it from assistive technologies, and place ARIA attributes on the actual form control.
Examples
Incorrect: aria-expanded on a span without a role
<spanaria-expanded="false">Menu</span>
The validator reports the missing role because aria-expanded isn't valid on a generic span.
Correct: Add a role (or use a button)
<spanrole="button"tabindex="0"aria-expanded="false">Menu</span>
Or, better yet, use a real button element:
<buttonaria-expanded="false">Menu</button>
Incorrect: aria-valuenow without the full set of range attributes and role
<spanclass="progress-indicator"aria-valuenow="50">50%</span>
Correct: Include the role and all required range attributes
<spanrole="progressbar"aria-valuenow="50"aria-valuemin="0"aria-valuemax="100">
50%
</span>
Or use the native <progress> element, which has built-in semantics:
<progressvalue="50"max="100">50%</progress>
Incorrect: aria-required on a decorative span
<labelfor="email">
<spanclass="required"aria-required="true">*</span>
</label>
<inputid="email"name="email"type="email">
The aria-required attribute belongs on the form control, not on the decorative asterisk.
Correct: Hide the decorative indicator and mark the input as required
<labelfor="email">
<spanclass="required"aria-hidden="true">*</span>
</label>
<inputid="email"name="email"type="email"aria-required="true">
If you also want screen readers to announce "required" as part of the label text, add visually hidden text:
<labelfor="email">
<spanaria-hidden="true">*</span>
<spanclass="visually-hidden">required</span>
</label>
<inputid="email"name="email"type="email"required>
The key takeaway: whenever you use ARIA state or property attributes on a span, make sure the element also has the correct role and all companion attributes required by that role. When a native HTML element already provides the semantics you need — such as <button>, <progress>, or <meter> — prefer it over a span with ARIA, as native elements are more robust and require less additional markup.
A span element has an implicit ARIA role of generic, and the aria-label attribute is not allowed on elements with that role.
The span element is a generic inline container with no semantic meaning. Its default ARIA role is generic, which is one of several roles that prohibit naming via aria-label or aria-labelledby. This restriction exists because screen readers are not expected to announce names for generic containers — adding aria-label to them creates an inconsistent and confusing experience for assistive technology users.
To fix this, you have two main options:
- Assign an explicit role to the
spanthat supports naming, such asrole="img",role="group",role="status", or any other role that allowsaria-label. - Use a different element that already has a semantic role supporting
aria-label, such as abutton,a,section, ornav.
If the span is purely decorative or used for styling, consider using aria-hidden="true" instead and placing accessible text elsewhere.
HTML Examples
❌ Invalid: aria-label on a plain span
<spanaria-label="Close">✕</span>
✅ Fixed: assign an appropriate role
<spanrole="img"aria-label="Close">✕</span>
✅ Fixed: use a semantic element instead
<buttonaria-label="Close">✕</button>
✅ Fixed: hide the decorative span and provide text another way
<button>
<spanaria-hidden="true">✕</span>
<spanclass="visually-hidden">Close</span>
</button>
A span element has an implicit ARIA role of generic, and the aria-labelledby attribute is not allowed on elements with that role.
The span element is a generic inline container with no semantic meaning. Its default ARIA role is generic, and the ARIA specification explicitly prohibits naming generic elements with aria-labelledby (or aria-label). This restriction exists because accessible names on generic containers create confusing experiences for assistive technology users — screen readers wouldn't know what kind of thing is being labeled.
To fix this, you have two main options:
- Add a meaningful
roleto thespanthat supportsaria-labelledby, such asrole="group",role="region", or any other role that accepts a label. - Use a more semantic element that already has an appropriate role, like a
section,nav, ordivwith an explicit role.
If the span doesn't truly need a label, simply remove the aria-labelledby attribute.
HTML Examples
❌ Invalid: aria-labelledby on a plain span
<spanid="label">Settings</span>
<spanaria-labelledby="label">
<inputtype="checkbox"id="opt1">
<labelfor="opt1">Enable notifications</label>
</span>
✅ Fix: Add an appropriate role
<spanid="label">Settings</span>
<spanrole="group"aria-labelledby="label">
<inputtype="checkbox"id="opt1">
<labelfor="opt1">Enable notifications</label>
</span>
✅ Fix: Use a semantic element instead
<spanid="label">Settings</span>
<fieldsetaria-labelledby="label">
<inputtype="checkbox"id="opt1">
<labelfor="opt1">Enable notifications</label>
</fieldset>
The <span> element is an inline container used to mark up a portion of text or a group of inline elements, typically for styling or scripting purposes. Like most HTML elements, it requires both an opening <span> tag and a closing </span> tag. When the closing tag is missing, the browser's HTML parser must determine where the element ends on its own. Different browsers may handle this differently, potentially wrapping more content inside the <span> than intended.
This is problematic for several reasons:
- Unexpected styling: If the
<span>has CSS applied to it (via aclass,id, or inlinestyle), the styles may bleed into sibling or subsequent elements that were never meant to be affected. - Broken document structure: An unclosed
<span>inside a<p>element can cause the parser to implicitly close and reopen elements in unexpected ways, distorting the DOM tree. - Accessibility concerns: Screen readers and assistive technologies rely on a well-formed DOM. An unclosed element can cause content to be grouped or announced incorrectly.
- Maintenance difficulty: Unclosed elements make the markup harder to read, debug, and maintain over time.
A common scenario is forgetting the closing tag when the <span> wraps only part of a paragraph's text. Another frequent cause is mismatched or misordered nesting, where the closing tags appear in the wrong sequence.
Examples
Unclosed <span> inside a paragraph
The closing </span> is missing entirely, so the browser has to guess where the span ends:
<!-- ❌ Bad: unclosed <span> -->
<p><spanclass="highlight">I'm forgetting something</p>
<p>Life goes on</p>
Add the missing </span> before the content that shouldn't be wrapped:
<!-- ✅ Good: <span> is properly closed -->
<p><spanclass="highlight">I'm forgetting something</span></p>
<p>Life goes on</p>
Incorrectly nested closing tags
Sometimes the closing tags are present but in the wrong order. Tags must be closed in the reverse order they were opened:
<!-- ❌ Bad: closing tags are misordered -->
<p><em><spanclass="note">Important text</em></span></p>
<!-- ✅ Good: tags closed in correct (reverse) order -->
<p><em><spanclass="note">Important text</span></em></p>
Multiple <span> elements with a missing close
When using several <span> elements in sequence, it's easy to lose track:
<!-- ❌ Bad: second <span> is never closed -->
<p>
<spanclass="first-name">Jane</span>
<spanclass="last-name">Doe
</p>
<!-- ✅ Good: all <span> elements are closed -->
<p>
<spanclass="first-name">Jane</span>
<spanclass="last-name">Doe</span>
</p>
Tips for avoiding unclosed elements
- Indent consistently. Proper indentation makes it much easier to spot a missing closing tag.
- Write both tags at once. When you type
<span>, immediately type</span>and then fill in the content between them. - Use an editor with auto-closing and bracket matching. Most modern code editors will highlight unmatched tags.
- Validate regularly. Run your markup through the W3C HTML Validator often, especially after larger edits.
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
Pro Trial
Full Pro access. Cancel anytime.
Start Pro Trial →Join teams across 40+ countries