m-

A design system that fully embraces web standards ๐Ÿค—

Think of it as HTML6

So modern it feels old!

Because UI should be fun ๐Ÿฅณ

It's freedom, baby. Yeah!

Standards #FTW

Code depressed? Call 1-800-MDASH

28 components, 220+ utility classes, and it's how small???

"Nothing is faster than nothing."
-Me

The AWS bill was $90 last month!!

You can stop reading this.

How many of these are there?

15, including this one.

Does it loop though?

No.

Mdash intends to leverage HTML, not replace it or try to outsmart it.
This makes Mdash ideal for all web projects and skill levels.
linkable

tiny 6kb

responsive

zero dependencies

very compatible
accessible
Quick start

This is the web. Let's use it.

or npm

Introduction

What is Mdash?

Mdash is a better kind of UI library. It's 100% standards-based and it's tiny.

Mdash components are comprised of standard HTML, custom HTML, and Custom Elements. As such, Mdash works with any framework (or no framework) and works with all types of web projects, like SSR, SPA, PWA, static site, and even popular email clients.

What makes Mdash different?

Quite literally, nothing. Nothing is exactly what makes Mdash different:

  • No new concepts or abstractions
  • No setup or configuration
  • No dependencies
  • No build step

It's just HTML plus custom HTML. As a result, no other UI library is as small or easy or familiar as Mdash.

Take a look around and compare Mdash's size and markup to see how nothing really is better.

Where did Mdash come from?

Mdash is the result of building design systems in large engineering organizations where - for better or worse - tech stacks and architectures vary wildly, but the products still need to share common UI elements. It was during this time the TAC CSS methodology was created with Mdash being the first open-source implementation.


Mdash is compatible with everything

Mdash can be used anywhere HTML is used because it is HTML. Larger organizations especially benefit here because unlike other UI libraries, Mdash will work with all your products regardless of technology stack. To demonstrate, here's code samples of 13 different technologies all using the same Mdash component:

*Framework compatibility with Custom Elements is being tracked on custom-elements-everywhere.com. Today, all but one works with this standard.

Performance

Mdash is fast! It is by all practical measures instant. The execution speed comes from leveraging native technology and reducing abstractions as much as possible in order to minimize code and retain browser optimizations. When it comes to code, nothing is faster than nothing.

In addition to execution speed, pages load faster because Mdash is so much smaller than other UI libraries:

Mdash 6.8 kb
Bootstrap 71.4 kb
Material Web 2 79.5 kb
Zurb Foundation 87.8 kb
React Bootstrap 103 kb
Material-UI 134 kb
Semantic UI 174 kb
Microsoft Fabric 244 kb
Shoelace 294 kb
Material Web 3 353 kb
Note: Sizes are min+gzip and include stylesheets, scripts, and runtime dependencies. They do not include icons. In other words, this is the overhead before you can write your first line of code.

Installation

CDN

Mdash is designed for production use with a CDN. Copy and paste the following three resources into the <head> section of your document and you're golden.

Icon preload

<link href="https://unpkg.com/m-@3.2.0/dist/m-.woff2" rel="preload" as="font" crossorigin>

The preload option tells the browser to start downloading the icon font now instead of waiting for the stylesheet to be parsed. If you don't use icons, then you don't need this.

Stylesheet

<link href="https://unpkg.com/m-@3.2.0/dist/m-.css" rel="stylesheet">

The stylesheet includes Mdash's custom properties, the component styles, and all the utility classes.

Custom Elements

<script src="https://unpkg.com/m-@3.2.0/dist/m-.js" defer></script>

The defer option tells the browser to download the script but delay its evaluation to avoid blocking document parsing.

NPM

If the CDN is not an option, you can install the Mdash package and bundle the files.

npm install m-

Built assets (those three files above) are located in /dist. The hyper optimization of inlining CSS is also possible with Mdash because its so small, so go for it you speed demon!

Browser support

Mdash works with the latest versions of all major browsers. Please file a bug if you see something not working as expected.

More about Mdash

The purpose

To return the UI layer back to its rightful owner: the web platform.

The UI layer - the pixels - should be built with the modern web platform. Technologies likes CSS Custom Properties, Web Components, and new HTML and JavaScript features can and should be used to create the UI layer. No dependencies needed!

The application layer - data, routing, business logic, services - should also be built with the modern web stack as well as 3rd-party libraries and frameworks.

Mdash gives you a complete set of modern compatible-with-everything UI components instantly available from a CDN. No downloads. No CLI. No configuration. No build steps. No prespiling the virtual tree-shake lint matrix, or whatever. It does this by embracing HTML (and CSS and JavaScript) and relentlessly leveraging it. The result is a fun and uniquely small design system with standards-level longevity.

Depending on the kind of project you're working on, you can use Mdash on its own or alongside your framework of choice. In the latter case, the framework is responsible for the structure and state of your application while HTML and Mdash supply the final layer of UI.

Frameworks are misused

The UI layer of web applications has been overcomplicated and made dependent upon non-standard frameworks and methodologies that, in the context of generic UI components, do not add value. Frameworks are best applied to application-centric problems like routing, state management, and high-level product-centric component structure. Frameworks are not ideal for pure UI-centric problems like layout, shared UI elements, and generic styles.

The m- prefix

A prefix is required for any custom HTML (tags or elements). The "m" is for markup, because Mdash is all about that beautiful declarative markup! And no the name "Mdash" doesn't have anything to do with Lodash. It was suggested the proper name be changed from "M-" to "Mdash" to help with search engine results.

Design philosophy

Mdash believes in and is committed to the web platform. Developing on the web is awesome because:

Mdash was designed with all that in mind. It's why CDN install is the first option and not buried or unavailable as if linking to other resources on the internet is a last resort. It's why Mdash components are built with standard, approachable tech and not a popular JavaScript library. It's why there is no special syntax or proprietary ideas and why there's no dependencies. It's why the project is open source.

The intent of the Mdash API design is to be familiar. Where possible, Mdash matches native HTML, e.g. type="", name="", disabled, and when there isn't a native example to follow Mdash strives to use familiar patterns, e.g. dismissible="false" for Alert was chosen because it feels similar to the native draggable="false" attribute. If you could cover up all the m- prefixes in your markup, the hope is you couldn't tell the difference between native HTML elements and Mdash.

It's for everyone!

The web is an open platform accessible to everyone and so a design system for the web shouldn't require advanced knowledge just to get started, and it shouldn't require a commitment to learn a specific framework's idioms and API in order to make full use of it. Mdash serves true beginners equally as well as it does professional software engineers. So, whether you're building your first website or a large scale application with millions of users, Mdash helps you quickly build UI with significantly less code and with the longevity that only comes with standards-based code.

Components

Accordion

List of expandable details elements

Demo

API

Tag

Name Type Content
m-accordion Custom tag Details children only

Guidelines

Opening all or one

Accordion does not control its details children, it only styles them. This means more than one details can be open at a time by default. Controlling this behavior can be managed by your app by adding/removing open to the details. Details elements publish a toggle event makes this easy.

It's worth noting that limiting an accordion to one details at a time may result in a poor UX for users who want to reference information in multiple details and would need to constantly re-open them as they do so.

Here's two examples showing an imperative solution and a more declarative state-driven solution with Vue:

// Imperative
// Declarative (using Vue)

Accessibility

All accessibility recommendations apply to the Details children.


Alert

Dismissible container for escalated messages

Demo

API

Tag

Name Type Content
m-alert Custom Element Any

Attributes

Name Value Description
type required
  • info
  • success
  • warn
  • error
Sets the type of the message. Error messages should use "error", cautionary messages should use "warn", positive messages should use "success", and important informational messages should use "info".
autodismiss
  • Boolean attribute
  • Number of seconds
If present, the Alert will automatically remove itself after 4 seconds. To increase or decrease the delay, set its value to the desired number of seconds.
dismissible
  • false
When set to "false", the dismiss button will not be displayed and the user can't dismiss the alert. The application can still directly remove the alert from the DOM or call dismiss() and autodismiss is also still effective.
icon Any icon name Adds the specified icon. Good pairings for each type are info for info, check_circle for success, warning for warn, and error for error, but use whatever is best for your alert.

Events

Name Detail Description
dismiss None Fires after alert has been dismissed. This happens when the user clicks the dismiss button, after autodismiss completes, or when dismiss() is called directly.

Methods

Signature Description
dismiss() Removes the alert from the DOM and fires dismiss event.

Guidelines

Alert vs. Dialog

Some information is so important it justifies interrupting the user experience. In those cases consider displaying it in a dialog instead of an alert.

Accessibility

When type is "warn" or "error" the ARIA alert role will be added automatically. Add aria-label="" to the dismiss button with something helpful like "Remove confirmation message".


Autocomplete

Text input for searching values or getting suggestions

Demo

API

Tags

Name Type Content
m-autocomplete Custom Element None
datalist Native element <option> elements

Attributes

Name Value Description
source required String The source of data to query for matches. This must be the id of a <datalist> element or the name of a custom source function. Learn how to create them below.
max Number Truncates the number of results to max. Autocomplete will overflow at about 10 visible results regardless of the max set.

Events

Name Details Description
select
{
  // Name of the source for easy reference
  source: '',

  // Value from matches
  value: '',

  // Id of match, if provided
  id: ''
}
      
Fires after a match is selected. Try it:

Guidelines

Creating data sources for autocomplete

Sources can come from a standard datalist element or a custom function added to the global MdashAutocomplete.

1. Datalist element

Include a standard datalist element in the DOM. Its id must match the Autocomplete's source. The options' value attributes and optional text are searched for matches. Here's an example:

2. Custom source function

These are asynchronous functions added to the static MdashAutocomplete.sources object. Here's another fruit example similar to the datalist above:

Tip: If the source's data is over the network, cache results client-side for faster lookup next time the same query is given. A simple object is often good enough, e.g. const matches = cache[query] || await fetchAndCacheResults(query), but the Cache API is another excellent option. You can also use max if it's beneficial while searching to know the maximum number of matches that will be shown.

{{> more-autocomplete}}

Accessibility

There are no extra recommendations for Autocomplete.


Badge

Display notification counts

Demo

API

Tag

Name Type Content
m-badge Custom tag Text or the count attribute

Attributes

Name Value Description
count Number A number to display. When zero or empty the badge will hide itself.

Guidelines

Badge hides itself

Badge can be used to display a count or text. If either is omitted, the badge will be set to display: none.

Localization

Localize the count value before setting it.

Accessibility

Use aria-labelledby or aria-label to provide context for the count.


Box

Static container for elevating content

Demo

Primary content Secondary content

Content outside a Box is considered neutral

See more examples

API

Tag

Name Type Content
m-box Custom tag Any

Attributes

Name Value Description
ord
  • secondary
Ordinal number word for describing the content's importance.

Guidelines

Nesting

Although possible, Boxes of the same ord should not be nested inside each other. Nesting a secondary Box inside a primary Box is ok:

Terms of service

Boxes of the same ord should not be nested inside each other. Nesting a secondary Box inside a primary Box is ok. You must agree to these guidelines to continue.

Because a Box header is more tricky than it seems (compensating for default padding), it doesn't get any special styles. However, it just takes a few classes to get a nice Box header:

Header
Content

Accessibility

There are no accessibility recommendations for Box.



Button

For triggering actions

Demo

Ordinal name

Disabled state

Link as button

Link

Remove type

Button group

API

Tag

Name Type Content
button Native element Any content, but none for remove button

Attributes

Name Value Description
ord required*
  • primary
  • secondary
  • tertiary
Ordinal number word for describing the button's precedence.

*The remove button does not require this.

type
  • submit
  • remove

Use submit when the button submits a form. Avoid <input type="submit">. See Submit button for more info

Remove is a custom button type. It's used by other components like alert and dialog and is intended for remove/close use cases (e.g. remove item from cart, close the dialog). See below for more info.

disabled Boolean attribute Disables the button. Link buttons also need tabindex="-1" to prevent activation by keyboard or assistive tech.
aria-pressed
  • true
  • false
  • mixed
Defines the button (secondary only) as a toggle button and sets its pressed state. The ariaPressed property works as well. See MDN for details.

See Button Group below to learn how to create a group of toggle buttons. It is recommended to use secondary buttons.

For <a> only:

role required button Styles the link as a button. Use this instead of <button> when your use case needs a real link.

Guidelines

Button or link?

If the action navigates to a new url, strive to use regular links, but if the link needs to look like a button then add the role="button" attribute. It's important that for these cases an actual link is used so the user can cmd + click or copy the link. If the action does not navigate the user to a new url, then use a regular button.

Custom remove type

Like other buttons, it is your app not Mdash that handles the click. Dialog, alert, and other components use remove button to create a consistent experience. You should use it for similar use cases to meet user expectations:

Alert uses remove button
Tag supports remove button
Dialog defines a slot for its remove button.

Your app can use the remove button:

Order summary

๐ŸŒฎ Taco w/ spicy pork
๐ŸŒฏ Burrito w/ steak, add guac
๐Ÿฅค Horchata, large, x2

Total $18.36

Changing the size of the icon can be done with a utility class:

Button Group

To create a button group, simply put toggle buttons in a container with role="group". Your app manages the aria-pressed state and determines if one or more can be pressed at a time.

Event delegation is useful for capturing all toggle button clicks and managing the pressed state. Also, optionally using the value or data-* attribute can help your app know what actions to take, e.g. setting a new filter and removing the others.

Custom button styles

Mdash leaves the plain button element untouched so you can freely use them without competing with Mdash styles (Mdash buttons require the ord attribute). Optionally add the all-unset utility class to remove user-agent styles from button, then customize as desired. For example:

Accessibility

The remove button should have an aria-label attribute and disabled link buttons should have tabindex="-1" to prevent activation.


Checkbox

Form element for selecting many options

Demo

Languages

See more examples

API

Tag

Name Type Content
input Native element None

Attributes

Name Value Description
name required String All checkboxes in a group use the same name.

Guidelines

Radio, checkbox, or select?

There's many uses cases where a single checkbox works very well and other use cases where 50 checkboxes makes sense too. If only one of the options can be selected, use radio or select.

Accessibility

Like other input elements, be sure to use for and id.


Container

Primary container element for page layout

Demo

Container is centered in its parent, has responsive padding, and its content will be contained according to maxwidth.

See more examples

API

Tag

Name Type Content
m-container Custom tag Any

Attributes

Name Value Description
maxwidth
  • lg default
  • md
  • sm
  • none
Sets the max width of the container, which includes some padding. "lg" grows up to 1536px, "md" starts at 800px and shrinks down, and "sm" starts at 576px and shrinks down.
Read more on maxwidth for details.

Guidelines

More on maxwidth

By default Container will grow up to 1,536 pixels wide, which is intended to make full use of a screen up to the equivalent of a 16" MacBook Pro. You can remove this limit with none.

In cases where there isn't a full page of content or maybe a more focused layout is desired, md is often the right width. On-boarding flows or a promotional page are good examples where medium would be useful. In some cases a very narrow and focused layout is needed, like a log in form. Use sm for these.

In all cases Container includes padding and centers itself in the viewport.

Multiple containers

A page can have multiple containers. This is most common when two components should share the same container characteristics, like max width and centered in the viewport, but need to have different backgrounds or other design elements that prevent sharing a single container. A site navigation is a good example:

LOGO

And the rest of the page's content is here in another container.

Note how this section and the site nav's content are aligned.

Other layout elements

In addition to Mdash's Container and Grid, HTML offers a number of semantic elements for layout. From W3Schools:

  • <header> Defines a header for a document or a section
  • <nav> Defines a container for navigation links
  • <section> Defines a section in a document
  • <article> Defines an independent self-contained article
  • <aside> Defines content aside from the content (like a sidebar)
  • <footer> Defines a footer for a document or a section

Accessibility

There are no accessibility recommendations for Container.


Details

Expandable container for progressive disclosure

Demo

See more examples

API

Tags

Name Type Content
details Native element <summary> as first child and any other content after that
summary Native element Any, but should be a โ€œsummary, caption, or legend for the rest of the contents"

Attributes

Name Value Description
open Boolean attribute When present, the details are shown (everything after summary). When removed details are hidden. If you want Details open by default, add this attribute.

Events

Name Detail Description
toggle None

Fires after Details was opened or closed. If e.currentTarget.open is true, then it was just opened, otherwise it was closed.

Guidelines

Is the content even necessary?

Although Details and Accordion are great tools for progressive disclosure, you should consider if the hidden content is necessary or if it could just be removed altogether.

Clickable elements inside summary

Some designs will include a button or other interactive element in <summary> that should not toggle the details. Most elements behave as normal without toggling Details, but some, like in the example below, need an event handler to preventDefault to stop this from happening:

Navigation

A common application of Details is an expandable navigation. Here's a working example:

Safari flexbox bug

Safari has a bug that prevents summary from being styled as a flex container. It looks like a fix is coming. If your summary needs flexbox, add a div:

Accessibility

The aria-expanded attribute is managed automatically. Pressing spacebar will open/close details.


Dialog

Modal content container

Demo

API

Tag

Name Type Content
dialog Native element Any. The first element with autofocus will receive focus when the dialog is opened.

Slot

Name Element Content
close type="remove" button None. See Close button for details.

Attributes

Name Value Description
open Boolean attribute Will open the Dialog when added or close when removed. If you want the dialog displayed "modelessly", you have to call the show method.

Events

Name Detail Description
close None MDN says: "Fired when the dialog is closed, whether with the escape key, the HTMLDialogElement.close() method, or via submitting a form within the dialog with method="dialog"."
cancel None MDN says: "Fired when the user dismisses the current open dialog with the escape key."

Methods

Signature Description
close([returnValue]) MDN says: "Closes the dialog. An optional DOMString may be passed as an argument, updating the returnValue of the the dialog." Also note that the Dialog and its contents are still present in the DOM (e.g. forms still have user-entered values, so reset it if that's what your use case requires).
show() MDN says: "Displays the dialog modelessly, i.e. still allowing interaction with content outside of the dialog."
showModal() MDN says: "Displays the dialog as a modal, over the top of any other dialogs that might be present. Interaction outside the dialog is blocked."

Guidelines

Close button

Dialog elements do not come with a button to close themselves, so Mdash includes style-only support for one. Add <button slot="close" type="remove"> and Mdash will style it for you, but your application must implement the click handler (e.g. calling the dialog's close method or removing its open attribute). Note that this is required only if a dialog doesn't implement any other buttons for closing. Here's a basic example:

Forms and DOM state

The state of the content is controlled by your application. Dialog does not change the state of its children other than moving them into a containing div on init. Forms and all other elements will initialize the way they are provided by the application and will continue to remain untouched even when the Dialog is closed. For example, if a Dialog is used to present a login form the application should remove the Dialog completely or reset the form after successful authentication. If left alone the Dialog and the login form inside it will contain the user's credentials. It's your content; you have to manage it.

Accessibility

The necessary ARIA attributes are added automatically; however, if your Dialog "contains an alert message" you should set role="alertdialog".


Dot

Status indicator

Demo

Information Success Warning Error Unknown

API

Tag

Name Type Content
m-dot Custom tag Text (optional)

Attributes

Name Value Description
type
  • info
  • success
  • warn
  • error
Sets the type of the indicator and has the same meaning as Alert's type.

Guidelines

Dot text

In most cases Dot should include text, but sometimes it's okay to have a Dot without text (see Accessibility). An Accordion of system summaries works as a good example:

Databases

All good!

API

All good!

Notification service

Increase in avgerage response time.

Unknown type

A Dot with an unknown type is useful when a generic dot is needed. Another use case is when your app wants to display a dot, but its type is still being determined. A Loader can help communicate in these situations:

Marcus is offline
Marcus is joining
Marcus is online

Accessibility

When there is no text use aria-label, e.g. <m-dot type="success" aria-label="Systems okay">.


Form elements

Basic elements used when creating forms

Demo

Must be at least 8 characters

See more examples

API

Tags

Name Type Content
form Native element Any
fieldset Native element Any
label Native element Text
small Native element Text for "side-comments" associated with an input. Read more
Note: Form controls are documented separately. See Input, Checkbox, Radio, Select, and Textarea.

Attributes

Nothing special to call out. See MDN: form attributes for the full list.

Events

Name Detail Description
submit None Fires when the form is submitted, which happens when the user clicks the submit button or hits enter.
input None Fires when the value of a text-like input or text area changes.
change None Fires when a value is committed, like selecting from a list, picking a file or date, checking a box, or blurring away from a text input.

Guidelines

Submit button vs. input button

Mdash recommends forms use <button type="submit"> instead of <input type="submit">. This is more semantic and provides greater control of the button style and content. For example, you can't put an Icon or Loader inside a submit input.

Why <fieldset>?

Rather than inventing new tags or using boilerplate divs and classes, Mdash leverages the native fieldset tag. Although it's historically uncommon to put just one input inside a fieldset, it is perfectly valid markup. Single or multiple input combinations of various types work perfectly well and results in the most clean and uniform form code:

Why <small>?

Understand that small's semantics have to do with its type of content and not font size or other visual characteristic. The HTML spec uses examples such as "disclaimers, caveats, legal restrictions, or copyrights", which is inline with this use case. Some examples:

183/200 characters left
Must include a number, special character, and be at least 8 characters long
Please call ahead for seasonal options

Accessibility

See Input, Checkbox, Radio, Select, and Textarea for their respective accessibility recommendations.


Grid

Responsive 12-column grid system

Demo

This row's columns... ...will auto-span. These two columns... ...span an explicit number of columns (4 and 8).

See more examples

API

Tag

Name Type Content
m-row Custom tag m-col children only
m-col Custom tag Any

Attributes

Name Value Description

For m-row only:

center Boolean attribute Centers the columns in the row.

For m-col only:

span [sm-|md-|lg-]1-12 Sets the number of columns to span on a 12 column grid. Use the screen size prefixes to resize on specific screen sizes, e.g. span="9 sm-12" span 12 columns on small screens, otherwise 9.
indent [sm-|md-|lg-]1-12 Sets the number of columns to indent on a 12 column grid. Use the screen size prefixes to resize on specific screen sizes. Indenting a column by 12 is only useful if you're animating that column back into view.
order [sm-|md-|lg-]1-12 Overrides the order of the column in its row. Use the screen size prefixes to reorder columns for specific screen sizes.

Guidelines

Nesting Grids

Grids can be nested inside other grids to create complex responsive layouts. Two or even three Grids deep are normal, but any more than that and your implementation could be poorly designed.

Simpler solutions

Although Grid is very capable, avoid the temptation to use it for everything. Sometimes a utility class is sufficient. Compare the three centered headings below to see how Grid may not always be the right solution:

Example 1

Example 2

Example 3

Accessibility

There are no accessibility recommendations for Grid.


Icons

Symbols used to enhance communication

Demo

API

Tag

Name Type Content
m-icon Custom tag None

Attributes

Name Value Description
name required See available icons below Sets the icon's symbol
fill Boolean attribute Uses filled version

Guidelines

Available icons

Mdash now uses Material Symbols Outlined. On that page, getting an icon name is easier if you click the icon, then select the Android tab, then copy the name.

Pair with some text

Icons should be used to enhance content not replace it. Users can misinterpret icons, so strive to accompany an icon with a label or other relevant content in close proximity.

John Doe
Profile Settings
Log out

If an icon is on its own, use the title attribute to explain what it symbolizes (e.g. "Your current location") or its action (e.g. "Reload this page", like your browser's refresh icon). Hover over the user icon for a clue:

Profile Settings
Log out

Why not SVG?

Implementing SVG icons requires a relatively expensive abstraction and because there is no visual or accessibility difference between SVG and glyph icons, that abstraction would be all cost and no benefit. Mdash icons require no JavaScript and so they save kilobytes and have maximum compatibility.

Accessibility

Because icons should be paired with some text (see above) they should reference that text with aria-labelledby attribute. In cases where there is no label, use aria-label and title.


Input

Form element for receiving user input

Demo

API

Tag

Name Type Content
input Native element None

Attributes

Name Value Description
type
  • text default
  • email
  • password
  • tel
  • number
  • file
  • more...
Sets the expected type of value. Take care to set email, tel, password, and number inputs based on use case to ensure optimal user experience.

See checkbox, radio, and range for details on those specific types.

disabled Boolean attribute Disables the input so it can't be changed or interacted with. It will be skipped when tabbing and its value will not be submitted with the form.
readonly Boolean attribute Makes the input read-only, which means the user can't change its value, but can still tab to it and copy the text. Its value will be submitted with the form.
invalid Boolean attribute Highlights input as having an invalid value. When the input is invalid it should have a small element explaining how to correct it. Validation is owned by your application not Mdash.
placeholder String Displays a message inside the input. Ideal for showing an expected format or sample value.
autofocus Boolean attribute If present, the browser will bring focus to this element on page load. Excellent for log in or search pages or whenever the first interaction is likely to type something.
autocomplete
Credit Card
cc-name
cc-number
cc-csc
cc-exp-month
cc-exp-year
Name
name
given-name
additional-name
family-name
Address
street-address
address-level1 (state or province)
address-level2 (city)
postal-code
country
Phone & Email
tel
email
These are required in order for the browser to autofill the form. Not all values are shown here! See the complete list and learn more at MDN: HTML autocomplete attribute.
Note: Not all input attributes are listed here. See MDN: Input element - Attributes for the full list.

Guidelines

Use the right type

It's very important to take the time to understand what values a user can enter in a given input and to use the right type for those values. If, for example, the input is for a membership number then using type="number" will present an easier to use numbers-only keyboard on most touch devices. The same goes for email and phone numbers. These UX optimizations are important to users and are virtually free to implement, so take a moment to ensure you're using the right input type.

Accessibility

Labels should use the for attribute to reference the id of its corresponding input. Inputs should use the right type (see above), autofocus, and autocomplete to improve their usability.


Keyboard

Represents text from an input device

Demo

Press โ‡งโŒ˜T to close window

Ctrl + N

API

Tag

Name Type Content
kbd Native element (documentation) Text or other kbd elements

Guidelines

Formatting

Mdash doesn't define a format for keyboard shortcuts because there are many popular ways to do it. Shortcuts are also platform-specific and so it is recommended to follow the format used by the user's OS. There are several ways to detect OS using the Navigator object.

Windows seems to use a format that includes + characters to indicate a combination of key presses, like Ctrl + C.

macOS seems to use a sequence of characters to indicate a combination of key presses, like โŒ˜C. Symbols are used for modifier keys (see below).

Be careful how you show the + character. Users can misinterpret it as representing a required key.

macOS Modifier Key Symbols

To create macOS keyboard shortcuts, copy these symbols:

Accessibility

There are no accessibility recommendations for the keyboard element.



Loader

Indicates a processing state

Demo

See more examples

API

Tag

Name Type Content
m-loader Custom tag Text (optional)

Attributes

Name Value Description
loading Boolean attribute When present the loader will rotate infinitely. Without it Loader remains still.

Guidelines

Loader text

In most cases the loader should have its own text or be in close proximity to a related message, like a button label. In some cases it's appropriate to have a loader on it's own without a message (in those cases see Accessibility).

Loader size

The default size works for most use cases, but you can enlarge or shrink Loader with the text size utility classes or set its own font size:

Nesting Loader

Loader will inherit font properties, so when it's nested in elements like button, anchor, or headings it looks as you would expect it to:

Accessibility

If a Loader doesn't have text it should use aria-labelledby or aria-label.



Radio input

Form element for picking one of many choices

Demo

Speed

See more examples

API

Tag

Name Type Content
input Native element None

Attributes

Name Value Description
name required String All radios in a group use the same name.

Guidelines

Radio, checkbox, or select?

If only one of the options can be selected, don't use checkbox. There are no hard rules, but generally radio is best for 2-4 choices and select (or Autocomplete) is better for more.

Accessibility

Like other input elements, be sure to use for and id.


Range Slider

Form element for fine-grained values

Demo

See more examples

API

Tags

Name Type Content
input[type="range"] Native element None
output Native element The value of range

Attributes

Name Value Description
min Number Minimum value, default is 0
max Number Maximum value, default is 100
step Number Sets the granularity of values, default is 1
orient coming soon vertical Displays the range vertically

Events

Name Detail Description
change None Fires after the value is committed.
input None Fires as the value changes.

Guidelines

Displaying range values

A common use case is to display the value of range as it updates. Mdash leaves that part of the design open for customization, but with the one requirement to use the standard <output> element to contain the value.

Here's two simple designs to get you started:

Accessibility

Use for and id as usual, but also use for on output elements. MDN has more details ARIA: Using the slider role.


Select

Drop-down list of choices

Demo

API

Tag

Name Type Content
select Native element <option> or <optgroup> children
optgroup Native element <option> children
option Native element Text

Attributes

Name Value Description
label String Displayed as the <optgroup> heading.
value String Used as the value of the element.
Note: Not all input attributes are listed here. See MDN: Select element - Attributes for the full list.

Events

Name Detail Description
change None Fired after an option was selected.

Guidelines

{{> more-autocomplete}}

Accessibility

Label should use the for attribute to reference the id of its corresponding select.


Separator

An element that divides sections of content or groups of menu items

Demo

Content


Content

Content

Content

Content

API

Tags

Name Type Content
hr Native element None

Attributes

Name Value Description
aria-orientation vertical

Displays the separator vertically. Must have a Flexbox or Grid parent (use display utility classes).

Guidelines

Where to use

Separators are useful in menus, toolbars, and written content.

Avoid using too many separators. Font size, font weight, whitespace, color contrast and other common visual characteristics are usually enough to create the needed distinction between elements.

Accessibility

The hr element has an implicit role="separator", so don't add it. There is also no need to set aria-orientation="horizontal" since it's the default.


Switch

A control for toggling binary values like on/off

Demo

See more examples

API

Tag

Name Type Content
input[type=checkbox] Native element None

Attributes

Name Value Description
role required switch Defines the checkbox as a switch.
checked Boolean attribute Standard checkbox attribute
disabled Boolean attribute Standard checkbox attribute

Events

Name Detail Description
input None Switch is a checkbox, so you can listen to the same standard input event.
change None Switch is a checkbox, so you can listen to the same standard change event.

Guidelines

Standalone or form

A switch can be used on its own or more commonly as a list of options in a form, like this:

Network settings

Currently unavailable

Accessibility

MDN says, "The first rule of ARIA is if a native HTML element or attribute has the semantics and behavior you require, use it instead of re-purposing an element and adding ARIA. Instead use the native HTML checkbox of <input type="checkbox">, which natively provides all the functionality required."


Table

Used for tabular data

Demo

Product
Socks $9.99
Shorts $19.99
Sweater $29.99
Shoes $49.99

API

Tags

Name Type Content
table Native element <thead> (optional) and <tbody>
thead Native element <tr> with <th> children
tbody Native element <tr> with <td> children, first child can be <th> for row headings
tfoot Native element <tr> with <td> children

Attributes

For <table> only:

Name Value Description
layout fixed Table cells are sized automatically by the browser. This doesn't always produce the desired layout, so use this option with colspan to control the layout. Note this option does not mean the table size won't change at all - it'll still respond nicely to screen size.
striped Boolean attribute Creates a visual distinction between alternate rows.

For <td> and <th> only:

colspan Number Defines how many columns the cell should span.

For <th> only:

aria-sort
  • ascending
  • descending
Defines that column as sorted. Your application is responsible for the sorting behavior.

Guidelines

Not for page layout

Use Grid for creating a page's layout. Do not use Table.

Accessibility

The sorted column must have the appropriate aria-sort value. When the user clicks a sort button, add aria-pressed="true" to it to indicate the pressed state.


Tabs

Master-detail pattern for navigating content

Demo

API

Tags

Name Type Content
m-tabs Custom tag button or a children

Attributes

Name Value Description

For m-tabs only:

scrollable Boolean attribute Makes the tab list horizontally scrollable. In some cases, like flex grow, overflow doesn't happen and so the parent element will need an explicit width (try the width-full utility class).

For tab only:

aria-selected true or false Set the selected tab to true. See Selecting tabs for examples.
disabled Boolean attribute Disables the tab.

Guidelines

Selecting tabs

Selecting the tab

Setting the selected tab is handled by your app. To select a tab, set aria-selected="true". There are a several approaches you could take to manage this, but event delegation comparing tabs to the clicked tab is probably the simplest without a framework:

That will only select the tab. Selecting the corresponding tab panel is explained next.

Showing a tab panel

Mdash assumes nothing about what happens when a tab is selected, which allows for multiple solutions like toggling the hidden attribute, directly changing the DOM, rendering different components, navigating to a new page, or something else. Here's an example of the typical use case of selecting a tab and showing its panel:

Here's the same thing with a more declarative approach using Riot.js, for example:

Accessibility

All of the necessary ARIA attributes are here in the example below:


Tag

Used for keywords, labeling, and filters

Demo

non-smoking WiFi pool free breakfast

API

Tag

Name Type Content
m-tag Custom tag Text, icon, or link and optional remove button for removable tags

Guidelines

Removable tags

Simply include a remove button and, using your preferred method, attach a click handler that removes the tag. Here's a basic example:

Accessibility

Use aria-label and title when the Tag only contains an icon:


Text area

Multi-line input

Demo

API

Tag

Name Type Content
textarea Native element Text

Attributes

Name Value Description
rows Number Sets a starting height that will accommodate the given number of rows, i.e. lines of text. This does not prevent additional lines from being typed and the user can also resize it to be taller or shorter.
maxlength Number Limits the number of characters allowed in the text area.
minlength Number Sets a minimum required number of characters in the text area.
Note: Not all text area attributes are listed here. See MDN: Text area element - Attributes for the full list.

Guidelines

Sensible row size

The default is three rows, i.e. three lines of text, but you should set a number that makes the most sense for your use case. For example, a chat app makes sense to take the default of three, but a customer feedback form should likely have 5-10. Note that text areas are resizable so the user can extend it further if needed, just take care to set a sensible default.

Accessibility

There are no accessibility recommendations for text area.

Typography

Code

For displaying text as code

Demo

Use the code element for inline code and the pre element for multiple lines of code:
const user = await fetch(url, init);
alert(user.name);

API

Tags

Name Type Content
code Native element Text
pre Native element Multi-line text

Guidelines

Security

Because these elements' content is code, take extra care to ensure you're preventing cross-site scripting. There's nothing that makes these elements more or less secure than any other, but in this case you know for sure you're dumping code into the DOM, so be careful!

Accessibility

You should use aria-label and/or aria-description to explain the code. For example:

fetch(url, init)

Headings

Headings elements

Demo

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6

API

Tags

Name Type Content
h1-h6 Native element Any
hgroup Native element One heading and one or more paragraphs

Guidelines

SEO

Using heading elements improves SEO and accessibility.

Accessibility

Strive to use the heading elements. If your situation doesn't allow for that, there is role="heading" and aria-level for defining the same.


Lists

Lists of data or content

Demo

  • Foo
  • Bar
  • Baz
  1. Foo
  2. Bar
  3. Baz
  • Foo
  • Bar
  • Baz
  1. Foo
  2. Bar
  3. Baz

API

Tags

Name Type Content
ul Native element <li> children
ol Native element <li> children
dl Native element <dt> and <dd> children

Attributes

Name Value Description
type
  • content
Defines a vertical list of content. Used for implementing designs like the master-detail pattern. See guideline below.

For ul only:

type
  • disc default
  • square
  • circle
  • none
Sets the marker type

For ol only:

type
  • 1 default
  • A
  • a
  • I
  • i
Sets the marker type
reversed Boolean attribute Reverses the order of items
start Number Sets the starting number

Guidelines

Lists are for lists

List elements should be used for real lists. It's tempting to overuse them for anything that repeats, so take time to think about some better alternatives to list elements that should be used instead. For example, one alternative is the nav element when building a navigation bar:

Content list type

The content list type is meant for building a vertical list of content.

Semantics and accessibility

To retain list semantics and accessibility, Mdash leverages the standard tags for content lists. Use the correct tag for the content, i.e. if order matters use ol, otherwise ul. Example:

Ordered, like steps in a recipe:

  1. Prep Cook rice the day before ๐Ÿš
  2. Step 1 Preheat wok on high heat, add 2 tbsp. oil ๐Ÿ”ฅ
  3. Step 2 Chop veggies and meat ๐Ÿง„๐Ÿฅฉ
  4. Step 3 Add everything to wok and stir fry ๐Ÿซ•
  5. Step 4 Top with fresh basil and serve ๐ŸŒฟ

Unordered, like notification preferences:

Nesting

Content list is designed to work wherever you need it. It can be used on its own or, for example, nested in a dialog, menu, or a box like this:

  • one
  • two
  • three

Alternatives

Be intentional. Not every vertical layout is a content list. Sometimes you don't need any particular component at all. In some cases you have tabular content, so use a table. In other cases you may just need horizontal rules, like this recreation of the GitHub sidebar:

README.md

About

The modern web's design system.

javascript css html fun custom-elements standards

Releases

31 tags

Packages

No packages published

Accessibility

Use the correct list parent elements for your use case, i.e. ol when the list order is meaningful and ul when it isn't.


Text

Paragraph and other text-based elements

Demo

This is a paragraph with some bold text and some italic text. More text variations can be done using the utility classes.

This is something somebody said.

This is for small print, side-comments, disclaimers, etc.

API

Tags

Name Type Content
p Native element Any
blockquote Native element p element for the actual quotation
small Native element Any, but intended to represent "side-comments...small print, like copyright and legal text, independent of its styled presentation."

Guidelines

Characters per line

It is recommend that lines of text be no more than 65-75 characters, including spaces (Designing With Type, James Craig). This makes for a more comfortable reading experience. Usability studies dating back to the 1970's show readers experience fatigue with text running longer than this.

To demonstrate, the paragraph above has no width limit while that same paragraph below maxes out at 75ch using the txt-maxlength utility class:

It is recommend that lines of text be no more than 65-75 characters, including spaces (Designing With Type, James Craig). This makes for a more comfortable reading experience. Usability studies dating back to the 1970's show readers experience fatigue with text running longer than this.

SEO

Using these text semantically will improve SEO and accessibility.

Accessibility

The smallest font size that Mdash has is 13px, which is the smallest recommended by WCAG 2.1. Any smaller can be illegible for some users.

Styles

Custom Properties

Design tokens referenced internally and externally for custom styles

Demo

My background and text colors reference custom properties

API

Colors

All color values are in the perceptually uniform color space to provide visually appealing colors while still achieving a11y contrast guidelines.

Name Example Description
--m-color-red-[1|2|3]
Used to denote something has or will go wrong.
--m-color-orange-[1|2|3]
Used for bringing attention to something that may not be desirable.
--m-color-blue-[1|2|3]
Used to highlight primary actions.
--m-color-green-[1|2|3]
Used to communicate something desirable has or will happen.
--m-color-gray-[7|8|9]
Used for text and high-contrast backgrounds.
--m-color-gray-[1|2|3|4|5|6]
Grayscale used for backgrounds, borders, and disabled state.
--m-color-primary-action
Used for links and other primary action elements.
--m-color-disabled-[bg|fg]
Disabled background and foreground.
--m-color-border
Default border color.

Spacing

The spacing sizes are based on Divine Proportions to help provide a natural rhythm. Padding, margin, and gap use these values.

Name Value Description
--m-space-[xs|sm|md|lg|xl] 6px, 12px, 18px, 30px, 48px Used for developing a visual rhythm between elements. Loosely follows Divine Proportion. You should use the padding and margin utility classes whenever possible and reserve use of --m-space-* for other spacing needs like position.

Element styles

All elements use these values for their relevant styles.

Name Value Description
--m-border-radius-[sm|md|full] 2px, 4px, fully round Used for border radius.

Breakpoints

Although custom properties are unfortunately not usable in media queries, these breakpoint values are useful elsewhere.

The large breakpoint and above are considered desktop-sized

Name Value Description
--m-breakpoint-sm 576px The portrait width of an iPhone X.
--m-breakpoint-md 768px The portrait width of an iPad.
--m-breakpoint-lg 992px The landscape width of an iPad.

Other

Name Value Description
--m-font-family 'Helvetica Neue', Arial The typefaces in use.
--m-max-content-width 1320px The max width used by Container. Wrapping your content in a m-container is more likely what you need, but you can use this if necessary.
--m-min-input-height 34px The min height used for form elements. Reference this if you'd like your element to match the height of inputs.

Guidelines

Do not hard code values

Facebook famously had 548 unique colors hard-coded 6,498 times across all their stylesheets. Custom properties are a modern CSS feature that helps you avoid such a mess by defining reusable values. Use custom properties instead of hard coding their values. Like this:

/* Do */ #contact \{ background-color: var(--m-color-gray-2); } /* Don't */ #contact \{ background-color: #dedede; }

Go here to learn more about custom properties and how to use them.

Accessibility

The colors and spacing meet accessibility requirements.


Utility Classes

220+ CSS shortcuts and utility classes

Demo

Homer
Homer J. Simpson
Safety Inspector
Chunkylover53@aol.com
555-7334

API

Categories: Display Flexbox Spacing Position Font Text Border Background Other

Display

Name Description
grid Shortcut for display: grid
inline-grid Shortcut for display: inline-grid
flex Shortcut for display: flex
inline-flex Shortcut for display: inline-flex
block Shortcut for display: block
inline-block Shortcut for display: inline-block
inline Shortcut for display: inline
hidden Shortcut for display: none. Consider using the HTML hidden attribute instead.

Flexbox

Name Description
flx-grow-0 Shortcut for flex-grow: 0
flx-grow-1 Shortcut for flex-grow: 1
flx-shrink-0 Shortcut for flex-shrink: 0
flx-shrink-1 Shortcut for flex-shrink: 1
flx-wrap Shortcut for flex-wrap: wrap
flx-col Shortcut for flex-direction: column
flx-row Shortcut for flex-direction: row
flx-basis-content Shortcut for flex-basic: content
justify-content-start Shortcut for justify-content: flex-start
justify-content-center Shortcut for justify-content: center
justify-content-between Shortcut for justify-content: between
justify-content-evenly Shortcut for justify-content: evenly
justify-content-around Shortcut for justify-content: around
justify-content-end Shortcut for justify-content: flex-end
align-items-center Shortcut for align-items: center
align-items-start Shortcut for align-items: flex-start
align-items-end Shortcut for align-items: flex-end
align-self-stretch Shortcut for align-self: stretch
align-self-center Shortcut for align-self: center
align-self-start Shortcut for align-self: flex-start
align-self-end Shortcut for align-self: flex-end
place-content-center Shortcut for place-content: center

Spacing

Name Description
pad-[xs|sm|md|lg|xl|0] Sets padding on all sides to the specified size. Sizes map to the space custom props and 0 really means zero.
pad-[t|r|b|l]-[xs|sm|md|lg|xl|0] Sets padding on the specified side (top, right, bottom, or left) in the specified size. Sizes map to the space custom props and 0 really means zero.
mar-[xs|sm|md|lg|xl|0|auto] Sets margin on all sides to the specified size. Sizes map to the space custom props and 0 really means zero.
mar-[t|r|b|l]-[xs|sm|md|lg|xl|0|auto] Sets margin on the specified side (top, right, bottom, or left) in the specified size. Sizes map to the space custom props and 0 really means zero.
gap-[xs|sm|md|lg|xl|0] Sets gap to the specified size. Sizes map to the space custom props and 0 really means zero.

Position

Name Description
pos-absolute Shortcut for position: absolute
pos-fixed Shortcut for position: fixed
pos-relative Shortcut for position: relative
pos-static Shortcut for position: static
pos-sticky Shortcut for position: sticky
pos-[t|r|b|l]-0 Shortcut for setting top, right, bottom, or left to zero

Font

Name Description
fnt-bold Shortcut for font-weight: 700
fnt-med Shortcut for font-weight: 500
fnt-reg Shortcut for font-weight: 400
fnt-light Shortcut for font-weight: 300
fnt-italic Shortcut for font-style: italic
fnt-mono Shortcut for font-family: monospace
fnt-normal Shortcut for font-style: normal

Text

Name Description
txt-left Shortcut for text-align: left
txt-right Shortcut for text-align: right
txt-center Shortcut for text-align: center
txt-justify Shortcut for text-align: justify
txt-lower Shortcut for text-transform: lowercase
txt-upper Shortcut for text-transform: uppercase
txt-caps Shortcut for text-transform: capitalize
txt-space Shortcut for letter-spacing: 2px
txt-truncate Truncates overflowing text and shows an ellipsis.
txt-nowrap Shortcut for white-space: nowrap. Good for forcing inline elements to stretch wide enough to accommodate the text.
txt-break-all Shortcut for word-break: break-all. Good for permitting characters to wrap when there's not enough room.
txt-break-word Shortcut for word-break: break-word. Good for permitting words to wrap when there's not enough room.
txt-maxlength Shortcut for max-width: 75ch. Good for limiting text line length to improve readability. See Readability: The Optimal Line Length
txt-noselect Prevents the user from selecting the text of this element.
txt-[xs|sm|md|lg|xl|xxl] Sets font-size.
txt-[color-name] Sets text color. The class names map to the color custom props, e.g. txt-red-dark => --m-color-red-dark.
txt-[info|success|warn|error] Sets text color to the alert type.

Border

Name Description
brd Adds a border.
brd-[t|r|b|l] Adds a border to the specified side.
brd-none Removes border from all sides.
brd-radius-[sm|md|full] Sets border radius. Use full to create a circle.
brd-[sm|md] Sets border width.
brd-dashed Sets border style to dashed. Good for placeholders and drop targets.

Background

Name Description
bg-clip-text Clips background to match foreground text.
bg-cover Background will be scaled to fill entire area of element. The aspect ratio is preserved, but the image may be cropped.
bg-contain Background will be scaled to fit size of element. The aspect ratio is preserved and the image will not be cropped.
bg-[color-name] Sets background color. The class names map to the color custom props, e.g. bg-red-dark => --m-color-red-dark.

Other

Name Description
pointer Shortcut for cursor: pointer. Useful when you want an element to seem clickable. Buttons and links already have pointer set.
height-full Shortcut for height: 100%
height-half Shortcut for height: 50%
height-min-0 Shortcut for min-height: 0
width-full Shortcut for width: 100%
width-half Shortcut for width: 50%
width-fit Shortcut for width: fit-content
width-min-0 Shortcut for min-width: 0
box-sizing-border Shortcut for box-sizing: border-box
box-sizing-content Shortcut for box-sizing: content-box
overflow-auto Shortcut for overflow: auto
overflow-hidden Shortcut for overflow: hidden
overflow-clip Shortcut for overflow: clip (good for rounded corners)
vis-hidden Shortcut for visibility: hidden
shadow Adds default shadow
content-vis-auto Shortcut for content-visibility: auto. Use on large sections of content not visible to the user after page load. The browser skips rendering and layout until needed thereby reducing the initial page rendering time.
left Shortcut for float: left
right Shortcut for float: right
clear Shortcut for clear: both
all-unset Shortcut for all: unset

Guidelines

Reduce CSS maintenance

You can drastically cut down and even eliminate your CSS maintenance by leveraging these classes as much as possible.

Create and use templates

The demo above with all those classes is fine on its own or even 2-3 copies sitting next to each other, but beyond that you'll want a way to reuse chunks of HTML.

Reuse is possible with the <template> element or template literals (see below). If standard tools aren't enough, templating partials or static components are another low-code option. Avoid the temptation to turn every chunk of HTML into a framework-dependent component.

export function userCardTemplate(imgUrl, name, title, email, phone) { return `
$\{name}
$\{title}
$\{email}
$\{phone}
` } const homerCard = userCardTemplate('/img/homer.webp', 'Homer J. Simpson', 'Safety Inspector', 'Chunkylover53@aol.com', '555-7334')

Accessibility

There are no accessibility recommendations for these classes.