This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Recipes

Opinionated compositions assembled from elements, patterns, and components.

1 - alert-dialog

Confirmation dialog structure

Use alert dialog parts for destructive or confirmation flows that need a clear action and cancel target.

<section>
  <button commandfor="delete-dialog" command="show-modal">
    Delete project
  </button>
  <dialog id="delete-dialog" role="alertdialog" closedby="closerequest">
    <h2>Delete project?</h2>
  </dialog>
</section>

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Alert Dialog</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    data-example="alert-dialog-basic alert-dialog-demo"
  >
    <section id="confirmation-dialog" class="visual-example">
      <button
        type="button"
        commandfor="confirmation-dialog-content"
        command="show-modal"
        variant="outline"
      >
        Show Dialog
      </button>
      <dialog
        role="alertdialog"
        id="confirmation-dialog-content"
        closedby="closerequest"
        aria-labelledby="confirmation-dialog-title"
        aria-describedby="confirmation-dialog-description"
        size="default"
      >
        <header>
          <h2 id="confirmation-dialog-title">Are you absolutely sure?</h2>
          <p id="confirmation-dialog-description">
            This action cannot be undone. This will permanently delete your
            account from our servers.
          </p>
        </header>
        <footer>
          <button
            type="button"
            commandfor="confirmation-dialog-content"
            command="close"
            variant="outline"
          >
            Cancel
          </button>
          <button
            type="button"
            commandfor="confirmation-dialog-content"
            command="close"
          >
            Continue
          </button>
        </footer>
      </dialog>
    </section>
  </body>
</html>

Sizes And Composition

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Alert Dialog Workflows</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="chatDeleted=false"
    data-example="alert-dialog-destructive alert-dialog-media alert-dialog-rtl alert-dialog-small alert-dialog-small-media"
  >
    <main class="alert-dialog-workflows">
      <div class="alert-dialog-trigger-grid" aria-label="Alert dialog examples">
        <section id="share-project-dialog">
          <button
            type="button"
            variant="outline"
            commandfor="share-project-dialog-content"
            command="show-modal"
          >
            Share Project
          </button>
          <dialog
            role="alertdialog"
            size="default"
            id="share-project-dialog-content"
            closedby="closerequest"
            aria-labelledby="share-project-dialog-title"
            aria-describedby="share-project-dialog-description"
          >
            <header>
              <figure aria-hidden="true">
                <svg viewBox="0 0 24 24" fill="none">
                  <circle
                    cx="12"
                    cy="12"
                    r="9"
                    stroke="currentColor"
                    stroke-width="2"
                  />
                  <path
                    d="M12 8v8M8 12h8"
                    stroke="currentColor"
                    stroke-width="2"
                    stroke-linecap="round"
                  />
                </svg>
              </figure>
              <h2 id="share-project-dialog-title">Share this project?</h2>
              <p id="share-project-dialog-description">
                Anyone with the link will be able to view and edit this project.
              </p>
            </header>
            <footer>
              <button
                type="button"
                variant="outline"
                commandfor="share-project-dialog-content"
                command="close"
              >
                Cancel
              </button>
              <button
                type="button"
                commandfor="share-project-dialog-content"
                command="close"
              >
                Share
              </button>
            </footer>
          </dialog>
        </section>

        <section id="accessory-dialog">
          <button
            type="button"
            variant="outline"
            commandfor="accessory-dialog-content"
            command="show-modal"
          >
            Show Small Dialog
          </button>
          <dialog
            role="alertdialog"
            size="sm"
            id="accessory-dialog-content"
            closedby="closerequest"
            aria-labelledby="accessory-dialog-title"
            aria-describedby="accessory-dialog-description"
          >
            <header>
              <h2 id="accessory-dialog-title">Allow accessory to connect?</h2>
              <p id="accessory-dialog-description">
                Do you want to allow the USB accessory to connect to this
                device?
              </p>
            </header>
            <footer>
              <button
                type="button"
                variant="outline"
                commandfor="accessory-dialog-content"
                command="close"
              >
                Don't allow
              </button>
              <button
                type="button"
                commandfor="accessory-dialog-content"
                command="close"
              >
                Allow
              </button>
            </footer>
          </dialog>
        </section>

        <section id="accessory-media-dialog">
          <button
            type="button"
            variant="outline"
            commandfor="accessory-media-dialog-content"
            command="show-modal"
          >
            Show Small Dialog With Media
          </button>
          <dialog
            role="alertdialog"
            size="sm"
            id="accessory-media-dialog-content"
            closedby="closerequest"
            aria-labelledby="accessory-media-dialog-title"
            aria-describedby="accessory-media-dialog-description"
          >
            <header>
              <figure aria-hidden="true">
                <svg viewBox="0 0 24 24" fill="none">
                  <path
                    d="m7 7 10 10-5 5V2l5 5L7 17"
                    stroke="currentColor"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                </svg>
              </figure>
              <h2 id="accessory-media-dialog-title">
                Allow accessory to connect?
              </h2>
              <p id="accessory-media-dialog-description">
                Do you want to allow the USB accessory to connect to this
                device?
              </p>
            </header>
            <footer>
              <button
                type="button"
                variant="outline"
                commandfor="accessory-media-dialog-content"
                command="close"
              >
                Don't allow
              </button>
              <button
                type="button"
                commandfor="accessory-media-dialog-content"
                command="close"
              >
                Allow
              </button>
            </footer>
          </dialog>
        </section>

        <section id="delete-chat-dialog">
          <button
            type="button"
            variant="destructive"
            commandfor="delete-chat-dialog-content"
            command="show-modal"
          >
            Delete Chat
          </button>
          <dialog
            role="alertdialog"
            size="sm"
            id="delete-chat-dialog-content"
            closedby="closerequest"
            aria-labelledby="delete-chat-dialog-title"
            aria-describedby="delete-chat-dialog-description"
          >
            <header>
              <figure class="alert-dialog-media-destructive" aria-hidden="true">
                <svg viewBox="0 0 24 24" fill="none">
                  <path
                    d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6M10 11v5M14 11v5"
                    stroke="currentColor"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                </svg>
              </figure>
              <h2 id="delete-chat-dialog-title">Delete chat?</h2>
              <p id="delete-chat-dialog-description">
                This will permanently delete this chat conversation. View
                <a href="#settings">Settings</a> to delete saved memories.
              </p>
            </header>
            <footer>
              <button
                type="button"
                variant="outline"
                commandfor="delete-chat-dialog-content"
                command="close"
              >
                Cancel
              </button>
              <button
                type="button"
                variant="destructive"
                ng-click="chatDeleted=true"
                commandfor="delete-chat-dialog-content"
                command="close"
              >
                Delete
              </button>
            </footer>
          </dialog>
        </section>

        <div class="alert-dialog-rtl-triggers" dir="rtl" lang="ar">
          <section id="rtl-confirmation-dialog">
            <button
              type="button"
              variant="outline"
              commandfor="rtl-confirmation-dialog-content"
              command="show-modal"
            >
              إظهار الحوار
            </button>
            <dialog
              role="alertdialog"
              size="default"
              id="rtl-confirmation-dialog-content"
              closedby="closerequest"
              aria-labelledby="rtl-confirmation-dialog-title"
              aria-describedby="rtl-confirmation-dialog-description"
            >
              <header>
                <h2 id="rtl-confirmation-dialog-title">هل أنت متأكد تمامًا؟</h2>
                <p id="rtl-confirmation-dialog-description">
                  لا يمكن التراجع عن هذا الإجراء. سيؤدي هذا إلى حذف حسابك
                  نهائيًا من خوادمنا.
                </p>
              </header>
              <footer>
                <button
                  type="button"
                  variant="outline"
                  commandfor="rtl-confirmation-dialog-content"
                  command="close"
                >
                  إلغاء
                </button>
                <button
                  type="button"
                  commandfor="rtl-confirmation-dialog-content"
                  command="close"
                >
                  متابعة
                </button>
              </footer>
            </dialog>
          </section>

          <section id="rtl-accessory-dialog">
            <button
              type="button"
              variant="outline"
              commandfor="rtl-accessory-dialog-content"
              command="show-modal"
            >
              إظهار الحوار (صغير)
            </button>
            <dialog
              role="alertdialog"
              size="sm"
              id="rtl-accessory-dialog-content"
              closedby="closerequest"
              aria-labelledby="rtl-accessory-dialog-title"
              aria-describedby="rtl-accessory-dialog-description"
            >
              <header>
                <figure aria-hidden="true">
                  <svg viewBox="0 0 24 24" fill="none">
                    <path
                      d="m7 7 10 10-5 5V2l5 5L7 17"
                      stroke="currentColor"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                  </svg>
                </figure>
                <h2 id="rtl-accessory-dialog-title">السماح للملحق بالاتصال؟</h2>
                <p id="rtl-accessory-dialog-description">
                  هل تريد السماح لملحق USB بالاتصال بهذا الجهاز؟
                </p>
              </header>
              <footer>
                <button
                  type="button"
                  variant="outline"
                  commandfor="rtl-accessory-dialog-content"
                  command="close"
                >
                  عدم السماح
                </button>
                <button
                  type="button"
                  commandfor="rtl-accessory-dialog-content"
                  command="close"
                >
                  السماح
                </button>
              </footer>
            </dialog>
          </section>
        </div>
      </div>
      <output class="output">
        Chat: {{ chatDeleted ? 'deleted' : 'available' }}
      </output>
    </main>
  </body>
</html>

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. A native dialog configured for decisions.

Anatomy

Root styling selector

  • dialog[role="alertdialog"]

Semantic structure

Use a native dialog with role="alertdialog" beside its invoker button. Close controls use command=close; semantic headers, figures, and footers need no anatomy classes or nested AngularCSS attributes.

API

Attributes and state

AttributeAccessPurpose
roleAuthoredExplicit semantic role when native HTML does not provide one.
sizeAuthoredCompact action layout: sm; omit for the default dialog layout.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

This styling hook does not define component-specific CSS custom properties.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

A native dialog opened with command=show-modal owns top-layer rendering, modal focus, Escape, background isolation, and trigger focus restoration. Use closedby=closerequest when pointer light-dismiss must be disabled. AngularCSS registers no alert-dialog directive.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Connect the native dialog to a concise title and consequence description with aria-labelledby and aria-describedby. Put the least destructive action first in focus order and use closedby=closerequest when outside dismissal would be unsafe.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.

2 - application-shell

Enterprise application header, navigation, and workspace

Compose a persistent application header, the existing Sidebar, and a semantic main workspace. Routing, permissions, and page content remain application-owned. The reference uses the Sidebar’s responsive attribute so navigation starts off-canvas on narrow screens without adding an application-specific class.

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Application Shell</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body ng-app="angular.css" ng-cloak data-example="application-shell-demo">
    <section class="application-shell">
      <header>
        <button
          size="icon-sm"
          variant="ghost"
          aria-label="Toggle navigation"
          aria-controls="enterprise-navigation"
        >
        </button>
        <a href="#workspace">Northstar Operations</a>
        <menu>
          <button size="sm" variant="outline">Help</button>
          <button size="icon-sm" variant="ghost" aria-label="Account">
            AL
          </button>
        </menu>
      </header>
      <aside
        id="enterprise-navigation"
        ng-sidebar
        collapsible="offcanvas"
        responsive
        aria-label="Primary navigation"
      >
        <div>
          <nav>
            <section>
              <h2>Workspace</h2>
              <ul>
                <li>
                  <a href="#overview" aria-current="page"
                    ><span>Overview</span></a
                  >
                </li>
                <li>
                  <a href="#customers"><span>Customers</span></a>
                </li>
                <li>
                  <a href="#orders"><span>Orders</span></a>
                </li>
              </ul>
            </section>
          </nav>
        </div>
      </aside>
      <main id="workspace">
        <article class="card">
          <header>
            <h1>Operations overview</h1>
            <p>Review current work and open exceptions.</p>
          </header>
          <section>All systems are operating normally.</section>
        </article>
      </main>
    </section>
  </body>
</html>

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. Application header, navigation, and workspace composition.

Anatomy

Root styling selector

  • .application-shell

Semantic structure

Use one .application-shell containing a direct semantic header, an existing Sidebar, and a direct main landmark. Existing components keep their own root selectors; no shell part classes are required.

API

Attributes and state

This component has no directive-specific attributes beyond its semantic HTML.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

This styling hook does not define component-specific CSS custom properties.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

Application Shell composes a semantic header, Sidebar, and main landmark. Routing, session state, permissions, responsive navigation policy, and page content remain application-owned.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Prefer semantic landmarks and native elements inside the layout. Any interactive handles or triggers must retain an accessible name and visible focus indicator.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.

3 - data-table

Sortable and filterable semantic table composition

Compose Table, Filter Bar, Pagination, and native controls around backend-owned rows. AngularCSS supplies the dense workspace presentation while AngularTS owns local bindings and the backend owns data operations.

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Data Table</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="query=''; order='customer'; page=1; state={loading:false,error:false,stale:true,selected:false,canArchive:false}; rows=[{id:'ORD-1048',customer:'Ada Lovelace',status:'Open',total:'€1,240',selected:false},{id:'ORD-1047',customer:'Grace Hopper',status:'Review',total:'€860',selected:false},{id:'ORD-1046',customer:'Edsger Dijkstra',status:'Complete',total:'€420',selected:false},{id:'ORD-1045',customer:'Margaret Hamilton',status:'Open',total:'€2,100',selected:false}]"
    data-example="data-table-demo"
  >
    <section
      class="data-table visual-example"
      aria-labelledby="orders-title"
      aria-busy="{{ state.loading }}"
    >
      <header>
        <div>
          <h2 id="orders-title">Orders</h2>
          <p>Recent customer orders</p>
        </div>
        <form role="search" aria-label="Filter orders">
          <fieldset>
            <legend class="visually-hidden">Filters</legend>
            <label>
              <span class="visually-hidden">Search orders</span>
              <input
                type="search"
                ng-model="query"
                placeholder="Search orders"
              />
            </label>
          </fieldset>
        </form>
      </header>
      <aside
        variant="warning"
        aria-live="polite"
        ng-if="state.stale && !state.loading && !state.error"
        role="alert"
      >
        <h3>Showing cached orders</h3>
        <p>The latest refresh did not complete. Values may be out of date.</p>
        <button
          size="xs"
          variant="ghost"
          type="button"
          ng-click="state.stale=false"
        >
          Dismiss
        </button>
      </aside>
      <aside
        variant="destructive"
        aria-live="assertive"
        ng-if="state.error"
        role="alert"
      >
        <h3>Orders could not be loaded</h3>
        <p>The service returned an error. Existing filters are preserved.</p>
        <button
          size="xs"
          variant="outline"
          type="button"
          ng-click="state.error=false; state.loading=true"
        >
          Retry
        </button>
      </aside>
      <menu aria-label="Selected order actions" ng-if="state.selected">
        <output>
          <span ng-repeat="row in rows | filter:{selected:true}" ng-show="$last"
            >{{ $index + 1 }}</span
          >
          selected
        </output>
        <button size="sm" variant="outline" type="button">Export</button>
        <button
          size="sm"
          type="button"
          ng-disabled="!state.canArchive"
          aria-describedby="orders-permission-note"
        >
          Archive
        </button>
      </menu>
      <figure>
        <section class="empty" ng-if="state.loading" aria-live="polite">
          <output class="spinner" aria-label="Loading orders"></output>
          <h3>Loading orders</h3>
          <p>Current filters and selection will be preserved.</p>
        </section>
        <table ng-if="!state.loading && !state.error">
          <caption>
            Orders currently available to your account.
          </caption>
          <thead>
            <tr>
              <th scope="col">
                <input
                  type="checkbox"
                  aria-label="Select all orders"
                  ng-model="state.selectAll"
                  ng-change="rows[0].selected=state.selectAll; rows[1].selected=state.selectAll; rows[2].selected=state.selectAll; rows[3].selected=state.selectAll; state.selected=state.selectAll"
                />
              </th>
              <th scope="col">
                <button
                  variant="ghost"
                  ng-click="order=order==='id' ? '-id' : 'id'"
                >
                  Order
                </button>
              </th>
              <th scope="col">
                <button
                  variant="ghost"
                  ng-click="order=order==='customer' ? '-customer' : 'customer'"
                >
                  Customer
                </button>
              </th>
              <th scope="col">Status</th>
              <th scope="col">Total</th>
            </tr>
          </thead>
          <tbody>
            <tr
              ng-repeat="row in rows | filter:query | orderBy:order | limitTo:2:(page - 1) * 2"
              aria-selected="{{ row.selected }}"
            >
              <td>
                <input
                  type="checkbox"
                  aria-label="Select {{ row.id }}"
                  ng-model="row.selected"
                  ng-change="state.selected=rows[0].selected || rows[1].selected || rows[2].selected || rows[3].selected"
                />
              </td>
              <th scope="row">{{ row.id }}</th>
              <td>{{ row.customer }}</td>
              <td><span class="badge">{{ row.status }}</span></td>
              <td>{{ row.total }}</td>
            </tr>
          </tbody>
        </table>
        <p ng-if="!state.loading && !state.error">No matching orders.</p>
      </figure>
      <footer>
        <div>
          <output
            ><span>0</span
            ><span ng-repeat="row in rows | filter:query" ng-show="$last"
              >{{ $index + 1 }}</span
            >
            orders</output
          >
          <p id="orders-permission-note" ng-if="!state.canArchive">
            Your role can export orders but cannot archive them.
          </p>
        </div>
        <nav class="pagination" aria-label="Order pages">
          <ul>
            <li>
              <a
                href="#page-1"
                ng-click="page=1"
                aria-current="{{ page === 1 ? 'page' : 'false' }}"
                >1</a
              >
            </li>
            <li>
              <a
                href="#page-2"
                ng-click="page=2"
                aria-current="{{ page === 2 ? 'page' : 'false' }}"
                >2</a
              >
            </li>
          </ul>
        </nav>
      </footer>
    </section>
  </body>
</html>

Backend-driven states

The recipe keeps service state in authored HTML so applications can bind it to their own requests and authorization model:

StateHTML contract
LoadingSet aria-busy="true" on .data-table and replace the table region with Spinner, Skeleton, or Progress content.
EmptyKeep a direct explanatory paragraph or compose Empty after the filtered tbody has no rows.
ErrorPlace an Alert with aria-live="assertive" before the data region and retain filters for retry.
Permission limitedDisable unavailable native actions and connect the reason with aria-describedby.
Stale dataPlace a warning Alert with aria-live="polite" before the data region.
Filtering and pagingBind Filter Bar controls and Pagination links to application-owned query and page state.
Selection and bulk actionBind native checkboxes to row aria-selected and reveal one shared action menu when rows are selected.

AngularCSS styles these compositions without defining a request, cache, authorization, or data-source API.

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. Semantic table with application-owned data operations.

Anatomy

Root styling selector

  • .data-table

Semantic structure

Use .data-table on a section containing a semantic header, a figure with a native table, and an optional footer. Compose Filter Bar, Pagination, Checkbox, Button, Badge, Empty, Skeleton, and Progress without data-table part classes.

API

Attributes and state

AttributeAccessPurpose
aria-selectedAuthoredSelected row state.
aria-sortAuthoredColumn sort state: ascending, descending, none, or other.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

VariablePurpose
--data-table-max-heightMaximum scrollable table height; defaults to 32rem.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

Data Table composes the native Table, Filter Bar, Pagination, and existing controls. AngularTS or the backend owns rows, sorting, filtering, selection, pagination, loading, and mutations.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Keep native table, caption, header scope, and cell relationships. Sorting controls are buttons and expose direction with aria-sort; selection uses labeled native checkboxes and row aria-selected only when needed.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.

4 - date-picker

Calendar and native popover composition

Compose Calendar with the native Popover API and an application-owned date value. This recipe packages the existing date workflow without adding another model, parser, or form directive.

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Date Picker</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="selectedDate='2026-09-05'"
    data-example="date-picker-demo"
  >
    <div class="date-picker visual-example">
      <label for="delivery-date">Delivery date</label>
      <span>
        <button
          id="delivery-date"
          type="button"
          variant="outline"
          popovertarget="delivery-date-calendar"
        >
          <span>{{ selectedDate ? selectedDate : 'Choose a date' }}</span
          ><span aria-hidden="true"></span>
        </button>
        <aside
          id="delivery-date-calendar"
          popover
          side="bottom"
          align="start"
          aria-label="Choose delivery date"
        >
          <section
            ng-calendar
            data-calendar-generated
            data-month="2026-09"
            data-value="{{ selectedDate }}"
            ng-on-angularcss:calendar-select="selectedDate=$event.detail.value"
          >
            <header>
              <button
                type="button"
                size="icon"
                variant="ghost"
                aria-label="Previous month"
              >
              </button>
              <h2></h2>
              <button
                type="button"
                size="icon"
                variant="ghost"
                aria-label="Next month"
              >
              </button>
            </header>
            <div></div>
          </section>
        </aside>
      </span>
      <p>The application owns parsing, validation, and the submitted value.</p>
    </div>
  </body>
</html>

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. Calendar, popover, and form-control composition.

Anatomy

Root styling selector

  • .date-picker

Semantic structure

Use .date-picker around a visible label and the existing Popover and Calendar roots. The trigger may display an AngularTS-formatted value; no date-picker part classes are required.

API

Attributes and state

AttributeAccessPurpose
aria-invalidAuthoredValidation state on the Date Picker root, reflected on its trigger border.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

This styling hook does not define component-specific CSS custom properties.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

Date Picker composes Calendar with the native Popover API and a native form control or button. Calendar owns date-grid mechanics; AngularTS owns the model, parsing, validation, formatting, and submitted value.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Give the trigger and calendar useful names, preserve visible focus, and expose the selected date as text. The composed Calendar retains its full keyboard contract inside the native popover.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.

5 - drawer

Bottom anchored drawer panels

Drawers are native modal dialogs with CSS edge placement and optional handle, header, scroll body, and footer. AngularTS owns values and actions inside them.

<section class="drawer">
  <button commandfor="goal-drawer" command="show-modal">Open Drawer</button>
  <dialog id="goal-drawer" side="bottom">
    <h2>Move Goal</h2>
    <p>Set your daily activity goal.</p>
    <button commandfor="goal-drawer" command="close">Cancel</button>
  </dialog>
</section>

Activity Goal

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Drawer</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="goal=350; submittedGoal=350"
    data-example="drawer-demo"
  >
    <main class="visual-example">
      <section id="goal-drawer" class="drawer">
        <button
          commandfor="goal-drawer-content"
          command="show-modal"
          variant="outline"
        >
          Open Drawer
        </button>
        <dialog
          id="goal-drawer-content"
          closedby="any"
          aria-labelledby="goal-drawer-title"
          aria-describedby="goal-drawer-description"
          side="bottom"
        >
          <article>
            <header>
              <h2 id="goal-drawer-title">Move Goal</h2>
              <p id="goal-drawer-description">Set your daily activity goal.</p>
            </header>
            <section>
              <menu>
                <button
                  variant="outline"
                  size="icon-sm"
                  aria-label="Decrease"
                  ng-disabled="goal <= 200"
                  ng-click="goal=goal-10"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="M5 12h14"></path>
                  </svg>
                </button>
                <output>
                  <strong ng-bind="goal"></strong>
                  <span>Calories/day</span>
                </output>
                <button
                  variant="outline"
                  size="icon-sm"
                  aria-label="Increase"
                  ng-disabled="goal>= 400"
                  ng-click="goal=goal+10"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="M12 5v14"></path>
                    <path d="M5 12h14"></path>
                  </svg>
                </button>
              </menu>
              <figure aria-hidden="true">
                <span style="--height: 80%"></span
                ><span style="--height: 60%"></span>
                <span style="--height: 40%"></span
                ><span style="--height: 60%"></span>
                <span style="--height: 40%"></span
                ><span style="--height: 56%"></span>
                <span style="--height: 38%"></span
                ><span style="--height: 48%"></span>
                <span style="--height: 60%"></span
                ><span style="--height: 40%"></span>
                <span style="--height: 56%"></span
                ><span style="--height: 38%"></span>
                <span style="--height: 70%"></span>
              </figure>
            </section>
            <footer>
              <button
                commandfor="goal-drawer-content"
                command="close"
                ng-click="submittedGoal=goal"
              >
                Submit
              </button>
              <button
                commandfor="goal-drawer-content"
                command="close"
                variant="outline"
              >
                Cancel
              </button>
            </footer>
          </article>
        </dialog>
      </section>
      <output class="drawer-output" aria-live="polite">
        Submitted goal: <span ng-bind="submittedGoal"></span>
      </output>
    </main>
  </body>
</html>

Responsive Dialog

The application chooses Dialog on desktop and Drawer on compact viewports while sharing the same AngularTS form model.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Drawer Dialog</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="profile={email:'team@angularcss.dev',username:'@angularcss'}"
    data-example="drawer-dialog"
  >
    <main class="drawer-dialog-demo visual-example">
      <div class="drawer-dialog-desktop">
        <section id="responsive-profile-dialog" class="dialog">
          <button
            variant="outline"
            type="button"
            commandfor="responsive-profile-dialog-content"
            command="show-modal"
          >
            Edit Profile
          </button>
          <dialog
            class="drawer-profile-dialog"
            id="responsive-profile-dialog-content"
            closedby="any"
            aria-labelledby="responsive-profile-dialog-title"
            aria-describedby="responsive-profile-dialog-description"
          >
            <header>
              <h2 id="responsive-profile-dialog-title">Edit profile</h2>
              <p id="responsive-profile-dialog-description">
                Make changes to your profile here. Click save when you're done.
              </p>
            </header>
            <form class="drawer-profile-form">
              <div class="drawer-profile-field">
                <label for="desktop-email">Email</label>
                <input
                  id="desktop-email"
                  type="email"
                  ng-model="profile.email"
                />
              </div>
              <div class="drawer-profile-field">
                <label for="desktop-username">Username</label>
                <input id="desktop-username" ng-model="profile.username" />
              </div>
              <button
                type="button"
                commandfor="responsive-profile-dialog-content"
                command="close"
              >
                Save changes
              </button>
            </form>
          </dialog>
        </section>
      </div>

      <div class="drawer-dialog-mobile">
        <section id="responsive-profile-drawer" class="drawer">
          <button
            variant="outline"
            type="button"
            commandfor="responsive-profile-drawer-content"
            command="show-modal"
          >
            Edit Profile
          </button>
          <dialog
            id="responsive-profile-drawer-content"
            closedby="any"
            aria-labelledby="responsive-profile-drawer-title"
            aria-describedby="responsive-profile-drawer-description"
            side="bottom"
          >
            <header>
              <h2 id="responsive-profile-drawer-title">Edit profile</h2>
              <p id="responsive-profile-drawer-description">
                Make changes to your profile here. Click save when you're done.
              </p>
            </header>
            <form class="drawer-profile-form drawer-profile-mobile">
              <div class="drawer-profile-field">
                <label for="mobile-email">Email</label>
                <input
                  id="mobile-email"
                  type="email"
                  ng-model="profile.email"
                />
              </div>
              <div class="drawer-profile-field">
                <label for="mobile-username">Username</label>
                <input id="mobile-username" ng-model="profile.username" />
              </div>
              <button
                type="button"
                commandfor="responsive-profile-drawer-content"
                command="close"
              >
                Save changes
              </button>
            </form>
            <footer>
              <button
                variant="outline"
                commandfor="responsive-profile-drawer-content"
                command="close"
              >
                Cancel
              </button>
            </footer>
          </dialog>
        </section>
      </div>
    </main>
  </body>
</html>

Sides

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Drawer Sides</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="paragraphs=[1,2,3,4,5,6,7,8,9,10]"
    data-example="drawer-sides"
  >
    <main class="drawer-sides-demo visual-example">
      <section side="top" class="drawer">
        <button
          variant="outline"
          type="button"
          commandfor="drawer-native-627-content"
          command="show-modal"
        >
          Top
        </button>
        <dialog
          size="half"
          id="drawer-native-627-content"
          closedby="any"
          aria-labelledby="drawer-native-627-title"
          aria-describedby="drawer-native-627-description"
          side="top"
        >
          <header>
            <h2 id="drawer-native-627-title">Top drawer</h2>
            <p id="drawer-native-627-description">
              Review activity from the top edge.
            </p>
          </header>
          <section class="drawer-side-copy">
            <p ng-repeat="item in paragraphs">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris.
            </p>
          </section>
          <footer>
            <button
              variant="outline"
              commandfor="drawer-native-627-content"
              command="close"
            >
              Cancel
            </button>
          </footer>
        </dialog>
      </section>

      <section side="right" class="drawer">
        <button
          variant="outline"
          type="button"
          commandfor="drawer-native-1891-content"
          command="show-modal"
        >
          Right
        </button>
        <dialog
          id="drawer-native-1891-content"
          closedby="any"
          aria-labelledby="drawer-native-1891-title"
          aria-describedby="drawer-native-1891-description"
          side="right"
        >
          <header>
            <h2 id="drawer-native-1891-title">Right drawer</h2>
            <p id="drawer-native-1891-description">
              Review activity from the right edge.
            </p>
          </header>
          <section class="drawer-side-copy">
            <p ng-repeat="item in paragraphs">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris.
            </p>
          </section>
          <footer>
            <button
              variant="outline"
              commandfor="drawer-native-1891-content"
              command="close"
            >
              Cancel
            </button>
          </footer>
        </dialog>
      </section>

      <section side="bottom" class="drawer">
        <button
          variant="outline"
          type="button"
          commandfor="drawer-native-3155-content"
          command="show-modal"
        >
          Bottom
        </button>
        <dialog
          size="half"
          id="drawer-native-3155-content"
          closedby="any"
          aria-labelledby="drawer-native-3155-title"
          aria-describedby="drawer-native-3155-description"
          side="bottom"
        >
          <header>
            <h2 id="drawer-native-3155-title">Bottom drawer</h2>
            <p id="drawer-native-3155-description">
              Review activity from the bottom edge.
            </p>
          </header>
          <section class="drawer-side-copy">
            <p ng-repeat="item in paragraphs">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris.
            </p>
          </section>
          <footer>
            <button
              variant="outline"
              commandfor="drawer-native-3155-content"
              command="close"
            >
              Cancel
            </button>
          </footer>
        </dialog>
      </section>

      <section side="left" class="drawer">
        <button
          variant="outline"
          type="button"
          commandfor="drawer-native-4485-content"
          command="show-modal"
        >
          Left
        </button>
        <dialog
          id="drawer-native-4485-content"
          closedby="any"
          aria-labelledby="drawer-native-4485-title"
          aria-describedby="drawer-native-4485-description"
          side="left"
        >
          <header>
            <h2 id="drawer-native-4485-title">Left drawer</h2>
            <p id="drawer-native-4485-description">
              Review activity from the left edge.
            </p>
          </header>
          <section class="drawer-side-copy">
            <p ng-repeat="item in paragraphs">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris.
            </p>
          </section>
          <footer>
            <button
              variant="outline"
              commandfor="drawer-native-4485-content"
              command="close"
            >
              Cancel
            </button>
          </footer>
        </dialog>
      </section>
    </main>
  </body>
</html>

Scrollable Content

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Drawer Scrollable</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="paragraphs=[1,2,3,4,5,6,7,8,9,10]"
    data-example="drawer-scrollable-content"
  >
    <main class="drawer-scroll-demo visual-example">
      <section id="scrollable-drawer" side="right" class="drawer">
        <button
          variant="outline"
          type="button"
          commandfor="scrollable-drawer-content"
          command="show-modal"
        >
          Scrollable Content
        </button>
        <dialog
          id="scrollable-drawer-content"
          closedby="any"
          aria-labelledby="scrollable-drawer-title"
          aria-describedby="scrollable-drawer-description"
          side="right"
        >
          <header>
            <h2 id="scrollable-drawer-title">Move Goal</h2>
            <p id="scrollable-drawer-description">
              Set your daily activity goal.
            </p>
          </header>
          <section class="drawer-scroll-copy">
            <p ng-repeat="item in paragraphs">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris.
            </p>
          </section>
          <footer>
            <button>Submit</button>
            <button
              variant="outline"
              commandfor="scrollable-drawer-content"
              command="close"
            >
              Cancel
            </button>
          </footer>
        </dialog>
      </section>
    </main>
  </body>
</html>

Right To Left

View source
<!doctype html>
<html lang="ar" dir="rtl">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Drawer Rtl</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="goal=350"
    data-example="drawer-rtl"
  >
    <main class="visual-example">
      <section id="rtl-goal-drawer" class="drawer">
        <button
          variant="outline"
          type="button"
          commandfor="rtl-goal-drawer-content"
          command="show-modal"
        >
          فتح الدرج
        </button>
        <dialog
          id="rtl-goal-drawer-content"
          closedby="any"
          aria-labelledby="rtl-goal-drawer-title"
          aria-describedby="rtl-goal-drawer-description"
          side="bottom"
        >
          <article>
            <header>
              <h2 id="rtl-goal-drawer-title">نقل الهدف</h2>
              <p id="rtl-goal-drawer-description">حدد هدف نشاطك اليومي.</p>
            </header>
            <section>
              <menu>
                <button
                  variant="outline"
                  size="icon-sm"
                  aria-label="تقليل"
                  ng-click="goal=goal-10"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="M5 12h14"></path>
                  </svg>
                </button>
                <output>
                  <strong ng-bind="goal"></strong>
                  <span>سعرات حرارية/يوم</span>
                </output>
                <button
                  variant="outline"
                  size="icon-sm"
                  aria-label="زيادة"
                  ng-click="goal=goal+10"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="M12 5v14"></path>
                    <path d="M5 12h14"></path>
                  </svg>
                </button>
              </menu>
              <figure aria-hidden="true">
                <span style="--height: 80%"></span
                ><span style="--height: 60%"></span>
                <span style="--height: 40%"></span
                ><span style="--height: 60%"></span>
                <span style="--height: 40%"></span
                ><span style="--height: 56%"></span>
                <span style="--height: 38%"></span
                ><span style="--height: 48%"></span>
                <span style="--height: 60%"></span
                ><span style="--height: 40%"></span>
                <span style="--height: 56%"></span
                ><span style="--height: 38%"></span>
                <span style="--height: 70%"></span>
              </figure>
            </section>
            <footer>
              <button>إرسال</button>
              <button
                variant="outline"
                commandfor="rtl-goal-drawer-content"
                command="close"
              >
                إلغاء
              </button>
            </footer>
          </article>
        </dialog>
      </section>
    </main>
  </body>
</html>

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. A native dialog placed at a viewport edge.

Anatomy

Root styling selector

  • .drawer

Semantic structure

Use .drawer as a wrapper containing a native invoker button and dialog with authored side. Close controls use command=close; the bottom handle is generated by CSS and no anatomy classes are required.

API

Attributes and state

AttributeAccessPurpose
dirAuthoredText and interaction direction: ltr or rtl.
sideAuthoredDialog edge: top, right, bottom, or left.
sizeAuthoredUse half for a half-height top or bottom drawer.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

This styling hook does not define component-specific CSS custom properties.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

A native dialog owns modal disclosure, focus, Escape, background isolation, and restoration. The concise authored side attribute selects CSS edge placement. AngularTS remains responsible for form models, goal values, validation, submission, responsive application composition, and authored content.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Use a visible title and description connected to the native dialog. Keep the physical edge as presentation only; content order, focus order, and inherited text direction remain semantic.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.

6 - form-layout

Responsive enterprise form composition

Arrange native fieldsets and existing Field patterns into responsive columns. Native validation and AngularTS forms remain the authoritative form model.

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Form Layout</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body ng-app="angular.css" ng-cloak data-example="form-layout-demo">
    <form
      class="form-layout visual-example"
      name="customerForm"
      novalidate
      ng-submit="saved=customerForm.valid"
    >
      <header>
        <h2>Create customer</h2>
        <p>Fields marked as required must be completed.</p>
      </header>
      <aside
        role="alert"
        ng-if="customerForm.submitted && customerForm.invalid"
      >
        <header><h3>Check the customer details</h3></header>
        <ul>
          <li><a href="#form-layout-name">Enter a customer name.</a></li>
        </ul>
      </aside>
      <fieldset>
        <legend>Customer details</legend>
        <div class="field">
          <label for="form-layout-name">Customer name</label>
          <input
            id="form-layout-name"
            name="customerName"
            required
            ng-model="customer.name"
          />
        </div>
        <div class="field">
          <label for="form-layout-email">Email</label>
          <input
            id="form-layout-email"
            type="email"
            name="email"
            ng-model="customer.email"
          />
        </div>
        <div class="field">
          <label for="form-layout-region">Region</label>
          <select
            id="form-layout-region"
            name="region"
            ng-model="customer.region"
          >
            <option value="">Choose a region</option>
            <option>Europe</option>
            <option>Americas</option>
          </select>
        </div>
        <div class="field">
          <label for="form-layout-reference">Reference</label>
          <input
            id="form-layout-reference"
            name="reference"
            ng-model="customer.reference"
          />
        </div>
      </fieldset>
      <footer>
        <button variant="outline" type="reset">Cancel</button>
        <button type="submit">Create customer</button>
      </footer>
      <output aria-live="polite" ng-if="saved"
        >Customer is ready to save.</output
      >
    </form>
  </body>
</html>

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. Responsive native form and field composition.

Anatomy

Root styling selector

  • form.form-layout

Semantic structure

Apply .form-layout to a native form containing semantic headers, fieldsets, Field patterns, an optional Validation Summary, and a footer. No form-layout part classes are required.

API

Attributes and state

AttributeAccessPurpose
columnsAuthoredPreferred desktop column count: 1, 2, or 3.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

This styling hook does not define component-specific CSS custom properties.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

Form Layout arranges native fieldsets and Field patterns. Native validation and AngularTS forms remain authoritative for values, errors, submission, and server responses.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Preserve DOM and focus order as the layout changes columns. Group related controls with fieldset and legend, connect errors to controls, and place a Validation Summary before invalid fields.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.

7 - master-detail

Resizable record list and detail workspace

Compose Resizable with semantic navigation and an article to browse records without losing context. AngularTS or routing owns the selected record and data.

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Master Detail</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body ng-app="angular.css" ng-cloak data-example="master-detail-demo">
    <main
      class="master-detail visual-example"
      ng-resizable-panel-group
      aria-label="Customer workspace"
    >
      <section style="--panel-size: 0.75">
        <nav aria-label="Customers">
          <header><h2>Customers</h2></header>
          <ul>
            <li>
              <a href="#ada" aria-current="page"
                ><strong>Ada Lovelace</strong
                ><small>Enterprise · Active</small></a
              >
            </li>
            <li>
              <a href="#grace"
                ><strong>Grace Hopper</strong
                ><small>Business · Review</small></a
              >
            </li>
            <li>
              <a href="#edsger"
                ><strong>Edsger Dijkstra</strong
                ><small>Enterprise · Active</small></a
              >
            </li>
          </ul>
        </nav>
      </section>
      <hr aria-label="Resize customer list" />
      <section style="--panel-size: 2">
        <article id="ada">
          <header>
            <h1>Ada Lovelace</h1>
            <p>Enterprise customer since 2024</p>
          </header>
          <section>
            <dl>
              <div>
                <dt>Email</dt>
                <dd>ada@example.com</dd>
              </div>
              <div>
                <dt>Region</dt>
                <dd>Europe</dd>
              </div>
              <div>
                <dt>Status</dt>
                <dd>Active</dd>
              </div>
            </dl>
          </section>
        </article>
      </section>
    </main>
  </body>
</html>

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. Responsive record list and detail workspace.

Anatomy

Root styling selector

  • .master-detail

Semantic structure

Apply .master-detail and ng-resizable-panel-group to the same root. Use two direct sections separated by a labeled hr; place semantic navigation in the first and record content in the second.

API

Attributes and state

AttributeAccessPurpose
orientationAuthoredResizable panel axis: horizontal or vertical; the recipe stacks on narrow viewports.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

This styling hook does not define component-specific CSS custom properties.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

Master–Detail composes Resizable with semantic navigation and record content. The application owns record selection, routing, data loading, responsive overlay policy, and persistence of panel sizes.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Use a labeled navigation landmark for the master list and a semantic article for detail content. Resizable handles retain separator semantics, while narrow layouts preserve the same reading order.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.

8 - sheet

Edge anchored overlay panels

Use a native dialog and declarative invoker. Author side on the dialog as right, left, top, or bottom; CSS owns only physical placement.

<section class="sheet">
  <button commandfor="profile-sheet" command="show-modal">Open</button>
  <dialog id="profile-sheet" side="right">
    <header>
      <h2>Edit profile</h2>
      <p>Update your account details.</p>
    </header>
    <section>Content</section>
    <footer>
      <button commandfor="profile-sheet" command="close">Close</button>
    </footer>
  </dialog>
</section>

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Sheet</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="profile={name:'Pedro Duarte',username:'@peduarte'}; savedName='Pedro Duarte'"
    data-example="sheet-demo"
  >
    <main class="visual-example">
      <section id="profile-sheet" class="sheet">
        <button
          commandfor="profile-sheet-content"
          command="show-modal"
          variant="outline"
        >
          Open
        </button>
        <dialog
          id="profile-sheet-content"
          closedby="any"
          aria-labelledby="profile-sheet-title"
          aria-describedby="profile-sheet-description"
          side="right"
        >
          <header>
            <h2 id="profile-sheet-title">Edit profile</h2>
            <p id="profile-sheet-description">
              Make changes to your profile here. Click save when you're done.
            </p>
          </header>
          <form method="dialog">
            <section>
              <label
                ><span>Name</span>
                <input id="sheet-demo-name" ng-model="profile.name" autofocus />
              </label>
              <label
                ><span>Username</span>
                <input id="sheet-demo-username" ng-model="profile.username" />
              </label>
            </section>
            <footer>
              <button
                type="button"
                commandfor="profile-sheet-content"
                command="close"
                ng-click="savedName=profile.name"
              >
                Save changes
              </button>
              <button
                type="button"
                commandfor="profile-sheet-content"
                command="close"
                variant="outline"
              >
                Close
              </button>
            </footer>
          </form>
          <button
            type="button"
            commandfor="profile-sheet-content"
            command="close"
            variant="ghost"
            size="icon-sm"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="M18 6 6 18"></path>
              <path d="m6 6 12 12"></path>
            </svg>
            <span class="visually-hidden">Close sheet</span>
          </button>
        </dialog>
      </section>
      <output class="sheet-output" aria-live="polite">
        Saved profile: <span ng-bind="savedName"></span>
      </output>
    </main>
  </body>
</html>

The example keeps profile values and save state in AngularTS. Sheet owns only modal disclosure, semantics, focus, and placement.

Without Corner Close

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Sheet No Close</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body ng-app="angular.css" ng-cloak data-example="sheet-no-close-button">
    <main class="visual-example">
      <section id="plain-sheet" class="sheet">
        <button
          variant="outline"
          type="button"
          commandfor="plain-sheet-content"
          command="show-modal"
        >
          Open Sheet
        </button>
        <dialog
          id="plain-sheet-content"
          closedby="any"
          aria-labelledby="plain-sheet-title"
          aria-describedby="plain-sheet-description"
          side="right"
        >
          <header>
            <h2 id="plain-sheet-title">No Close Button</h2>
            <p id="plain-sheet-description">
              This sheet doesn't have a close button in the top-right corner.
              Click outside to close.
            </p>
          </header>
        </dialog>
      </section>
    </main>
  </body>
</html>

The panel intentionally omits close controls and remains dismissible through the exact overlay or Escape.

Sides

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Sheet Sides</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="paragraphs=[1,2,3,4,5,6,7,8,9,10]"
    data-example="sheet-side"
  >
    <main class="sheet-sides-demo visual-example">
      <section side="top" class="sheet">
        <button
          variant="outline"
          type="button"
          commandfor="sheet-native-624-content"
          command="show-modal"
        >
          Top
        </button>
        <dialog
          size="half"
          id="sheet-native-624-content"
          closedby="any"
          aria-labelledby="sheet-native-624-title"
          aria-describedby="sheet-native-624-description"
          side="top"
        >
          <header>
            <h2 id="sheet-native-624-title">Edit profile</h2>
            <p id="sheet-native-624-description">
              Make changes to your profile here. Click save when you're done.
            </p>
          </header>
          <section class="sheet-side-copy">
            <p ng-repeat="item in paragraphs">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris.
            </p>
          </section>
          <footer>
            <button>Save changes</button>
            <button
              variant="outline"
              commandfor="sheet-native-624-content"
              command="close"
            >
              Cancel
            </button>
          </footer>
        </dialog>
      </section>
      <section side="right" class="sheet">
        <button
          variant="outline"
          type="button"
          commandfor="sheet-native-1974-content"
          command="show-modal"
        >
          Right
        </button>
        <dialog
          id="sheet-native-1974-content"
          closedby="any"
          aria-labelledby="sheet-native-1974-title"
          aria-describedby="sheet-native-1974-description"
          side="right"
        >
          <header>
            <h2 id="sheet-native-1974-title">Edit profile</h2>
            <p id="sheet-native-1974-description">
              Make changes to your profile here. Click save when you're done.
            </p>
          </header>
          <section class="sheet-side-copy">
            <p ng-repeat="item in paragraphs">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris.
            </p>
          </section>
          <footer>
            <button>Save changes</button>
            <button
              variant="outline"
              commandfor="sheet-native-1974-content"
              command="close"
            >
              Cancel
            </button>
          </footer>
        </dialog>
      </section>
      <section side="bottom" class="sheet">
        <button
          variant="outline"
          type="button"
          commandfor="sheet-native-3320-content"
          command="show-modal"
        >
          Bottom
        </button>
        <dialog
          size="half"
          id="sheet-native-3320-content"
          closedby="any"
          aria-labelledby="sheet-native-3320-title"
          aria-describedby="sheet-native-3320-description"
          side="bottom"
        >
          <header>
            <h2 id="sheet-native-3320-title">Edit profile</h2>
            <p id="sheet-native-3320-description">
              Make changes to your profile here. Click save when you're done.
            </p>
          </header>
          <section class="sheet-side-copy">
            <p ng-repeat="item in paragraphs">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris.
            </p>
          </section>
          <footer>
            <button>Save changes</button>
            <button
              variant="outline"
              commandfor="sheet-native-3320-content"
              command="close"
            >
              Cancel
            </button>
          </footer>
        </dialog>
      </section>
      <section side="left" class="sheet">
        <button
          variant="outline"
          type="button"
          commandfor="sheet-native-4686-content"
          command="show-modal"
        >
          Left
        </button>
        <dialog
          id="sheet-native-4686-content"
          closedby="any"
          aria-labelledby="sheet-native-4686-title"
          aria-describedby="sheet-native-4686-description"
          side="left"
        >
          <header>
            <h2 id="sheet-native-4686-title">Edit profile</h2>
            <p id="sheet-native-4686-description">
              Make changes to your profile here. Click save when you're done.
            </p>
          </header>
          <section class="sheet-side-copy">
            <p ng-repeat="item in paragraphs">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris.
            </p>
          </section>
          <footer>
            <button>Save changes</button>
            <button
              variant="outline"
              commandfor="sheet-native-4686-content"
              command="close"
            >
              Cancel
            </button>
          </footer>
        </dialog>
      </section>
    </main>
  </body>
</html>

Top and bottom panels use a half-viewport maximum in this composition. The scrolling body remains independent from the header and footer.

RTL

View source
<!doctype html>
<html lang="ar" dir="rtl">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Sheet Rtl</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="profile={name:'Pedro Duarte',username:'peduarte'}; savedName='Pedro Duarte'"
    data-example="sheet-rtl"
  >
    <main class="visual-example">
      <section id="rtl-profile-sheet" side="left" class="sheet">
        <button
          variant="outline"
          type="button"
          commandfor="rtl-profile-sheet-content"
          command="show-modal"
        >
          فتح
        </button>
        <dialog
          id="rtl-profile-sheet-content"
          closedby="any"
          aria-labelledby="rtl-profile-sheet-title"
          aria-describedby="rtl-profile-sheet-description"
          side="left"
        >
          <header>
            <h2 id="rtl-profile-sheet-title">تعديل الملف الشخصي</h2>
            <p id="rtl-profile-sheet-description">
              قم بإجراء تغييرات على ملفك الشخصي هنا. انقر حفظ عند الانتهاء.
            </p>
          </header>
          <form method="dialog">
            <section>
              <label
                ><span>الاسم</span>
                <input id="sheet-rtl-name" ng-model="profile.name" autofocus />
              </label>
              <label
                ><span>اسم المستخدم</span>
                <input id="sheet-rtl-username" ng-model="profile.username" />
              </label>
            </section>
            <footer>
              <button
                type="button"
                ng-click="savedName=profile.name"
                commandfor="rtl-profile-sheet-content"
                command="close"
              >
                حفظ التغييرات
              </button>
              <button
                type="button"
                variant="outline"
                commandfor="rtl-profile-sheet-content"
                command="close"
              >
                إغلاق
              </button>
            </footer>
          </form>
          <button
            variant="ghost"
            size="icon-sm"
            commandfor="rtl-profile-sheet-content"
            command="close"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="M18 6 6 18"></path>
              <path d="m6 6 12 12"></path>
            </svg>
            <span class="visually-hidden">إغلاق اللوحة</span>
          </button>
        </dialog>
      </section>
      <output class="sheet-output" aria-live="polite">
        الملف المحفوظ: <span ng-bind="savedName"></span>
      </output>
    </main>
  </body>
</html>

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. A native dialog presented as a side sheet.

Anatomy

Root styling selector

  • .sheet

Semantic structure

Use .sheet as a wrapper containing a native invoker button and dialog with authored side. Close controls use command=close; semantic headers, sections, forms, and footers need no anatomy classes.

API

Attributes and state

AttributeAccessPurpose
commandAuthoredNative invoker action such as show-modal or close.
dirAuthoredText and interaction direction: ltr or rtl.
sideAuthoredDialog edge: top, right, bottom, or left.
sizeAuthoredUse half for a half-height top or bottom sheet.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

This styling hook does not define component-specific CSS custom properties.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

A native dialog owns modal disclosure, focus, Escape, background isolation, and restoration. The concise authored side attribute selects CSS edge placement. AngularTS remains responsible for form values, validation, submission, language, and authored content.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Use a visible title and description connected to the native dialog. Keep the physical edge as presentation only; content order, focus order, and inherited text direction remain semantic.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.