<header id="example-header--compact" class="cwf-header cwf-header--gold cwf-header--compact">
    <div class="cwf-header__container">
        <div class="cwf-header__title cwf-header__title--reverse">
            <h1 class="cwf-header__department"> <a class="cwf-header__link" href="/">
                    Example Site
                </a>
            </h1>
        </div>
        <div class="cwf-header__controls"> <button class="cwf-header__toggle" aria-controls="cwf-header__search" aria-keyshortcuts="/">
                <i class="fas fa-search cwf-header__icon"></i>
                <span class="cwf-header__text">Search</span>
            </button>
        </div>
        <div id="cwf-header__features" class="cwf-header__features">
            <button class="cwf-header__exit" aria-controls="cwf-header__features">
                Close
            </button>
            <form id="cwf-header__search" class="cwf-header__search" action="https://search.vcu.edu/s/search.html">
                <input type="hidden" name="collection" value="vcu-meta" /> <input type="hidden" name="clive" value="vcu-ts" />
                <input type="text" name="query" id="query" class="cwf-header__input" required />
                <button type="submit" class="cwf-header__submit">
                    <i class="fas fa-search"></i>
                </button>
                <label for="query" class="cwf-header__label">
                    Search
                </label>
            </form>
        </div>
    </div>
</header>
<header id="{{ id ?? 'cwf-header' }}"
    class="cwf-header cwf-header--{{ theme }} {{
    compact
        ? 'cwf-header--compact'
    }}">
    <div class="cwf-header__container">
        <div class="cwf-header__title cwf-header__title--reverse">
            <h1 class="cwf-header__department">
                {%- if parent and parent.name and not parent.url %}
                    <a class="cwf-header__link cwf-header__link--multi-line"
                        href="{{ unit.url }}">
                        <span class="h2 cwf-header__parent">
                            {{ parent.name }}
                        </span>
                        <span>{{ unit.name }}</span>
                    </a>
                {% else %}
                    <a class="cwf-header__link" href="{{ unit.url }}">
                        {{ unit.name }}
                    </a>
                {% endif %}
            </h1>
            {%- if parent and parent.name and parent.url %}
                <h2 class="cwf-header__parent">
                    <a class="cwf-header__link" href="{{ parent.url }}">
                        {{ parent.name }}
                    </a>
                </h2>
            {% endif -%}
        </div>
        {%- if quick_links or search %}
            <div class="cwf-header__controls">
                {%- if quick_links %}
                    <button class="cwf-header__toggle"
                        aria-controls="cwf-header__nav"
                        aria-keyshortcuts=".">
                        <i class="fas fa-ellipsis-v cwf-header__icon"></i>
                        <span class="cwf-header__text">Links</span>
                    </button>
                {% endif %} {%- if search %}
                    <button class="cwf-header__toggle"
                        aria-controls="cwf-header__search"
                        aria-keyshortcuts="/">
                        <i class="fas fa-search cwf-header__icon"></i>
                        <span class="cwf-header__text">Search</span>
                    </button>
                {% endif %}
            </div>
            <div id="cwf-header__features" class="cwf-header__features">
                <button class="cwf-header__exit"
                    aria-controls="cwf-header__features">
                    Close
                </button>
                {%- if quick_links %}
                    <nav id="cwf-header__nav"
                        class="cwf-header__nav"
                        aria-label="Quick links">
                        {%- for quick_link in quick_links %}
                            <a href="{{ quick_link.url }}"
                                class="cwf-header__link">
                                {{ quick_link.name }}
                            </a>
                        {%- endfor %}
                    </nav>
                {%- endif %} {%- if search %}
                    <form id="cwf-header__search"
                        class="cwf-header__search"
                        action="https://search.vcu.edu/s/search.html">
                        <input type="hidden"
                            name="collection"
                            value="vcu-meta" />
                        {%- if search.collection %}
                            <input type="hidden"
                                name="clive"
                                value="{{ search.collection }}" />
                        {% endif -%} {%- if search.scope %}
                            <input type="hidden"
                                name="scope"
                                value="{{ search.scope }}" />
                        {% endif -%}
                        <input type="text"
                            name="query"
                            id="query"
                            class="cwf-header__input"
                            required />
                        <button type="submit" class="cwf-header__submit">
                            <i class="fas fa-search"></i>
                        </button>
                        <label for="query" class="cwf-header__label">
                            Search
                        </label>
                    </form>
                {% endif %}
            </div>
        {% endif %}
    </div>
</header>
{
  "unit": {
    "name": "Example Site",
    "url": "/"
  },
  "compact": true,
  "theme": "gold",
  "search": {
    "collection": "vcu-ts",
    "scope": null
  },
  "id": "example-header--compact"
}
  • Content:
    // Header component styles
    
    @import "../../shared/animation";
    @import "../../shared/media";
    @import "../../shared/style";
    @import "../../shared/theme";
    
    .cwf-header {
        display: flex;
        justify-content: center;
        padding-top: 1rem;
        padding-bottom: 1rem;
        background-color: var(--cwf-header--background-color);
        font-family: theme__font--sans-serif();
    
        @include media__breakpoint {
            min-height: 8rem;
            padding-top: 2rem;
            padding-bottom: 2rem;
        }
    }
    
    // Header white colors
    $header--white--background-color: style__color(white) !default;
    $header--white__link--color: style__color(gray-darkest) !default;
    $header--white__link--hover-focus--color: style__color(black) !default;
    $header--white__input--background-color: style__color(white-dark) !default;
    $header--white__input--focus--background-color: style__color(white) !default;
    $header--white__input--border-color: style__color(white-darkest) !default;
    $header--white__input--hover--border-color: style__color(
        gray-lightest
    ) !default;
    $header--white__input--focus--border-color: style__color(gray-darkest) !default;
    $header--white__input--color: style__color(gray-darkest) !default;
    $header--white__submit--border-color: style__color(white-darkest) !default;
    $header--white__submit--hover--border-color: style__color(
        gray-lightest
    ) !default;
    $header--white__submit--focus--border-color: style__color(
        gray-darkest
    ) !default;
    $header--white__label--color: style__color(gray-lightest) !default;
    
    // Header gray colors
    $header--gray--background-color: style__color(white-dark) !default;
    $header--gray__link--color: style__color(gray-darkest) !default;
    $header--gray__link--hover-focus--color: style__color(black) !default;
    $header--gray__input--background-color: style__color(white) !default;
    $header--gray__input--focus--background-color: style__color(white) !default;
    $header--gray__input--border-color: darken(
        style__color(white-darkest),
        10%
    ) !default;
    $header--gray__input--hover--border-color: darken(
        style__color(gray-lightest),
        10%
    ) !default;
    $header--gray__input--focus--border-color: style__color(gray-darkest) !default;
    $header--gray__input--color: style__color(gray-darkest) !default;
    $header--gray__submit--border-color: style__color(white-darkest) !default;
    $header--gray__submit--hover--border-color: style__color(
        gray-lightest
    ) !default;
    $header--gray__submit--focus--border-color: style__color(gray-darkest) !default;
    $header--gray__label--color: style__color(gray-lightest) !default;
    
    // Header gold colors
    $header--gold--background-color: style__color(gold) !default;
    $header--gold__link--color: style__color(gray-darkest) !default;
    $header--gold__link--hover-focus--color: style__color(black) !default;
    $header--gold__input--background-color: style__color(white) !default;
    $header--gold__input--focus--background-color: style__color(white) !default;
    $header--gold__input--border-color: darken(style__color(gold), 10%) !default;
    $header--gold__input--hover--border-color: darken(
        style__color(gold),
        15%
    ) !default;
    $header--gold__input--focus--border-color: style__color(gray-darkest) !default;
    $header--gold__input--color: style__color(gray-darkest) !default;
    $header--gold__submit--border-color: style__color(white-darkest) !default;
    $header--gold__submit--hover--border-color: style__color(
        gray-lightest
    ) !default;
    $header--gold__submit--focus--border-color: style__color(gray-darkest) !default;
    $header--gold__label--color: style__color(gray-lightest) !default;
    
    // Header dark colors
    $header--dark--background-color: style__color(gray-dark) !default;
    $header--dark__link--color: style__color(white) !default;
    $header--dark__link--hover-focus--color: style__color(white-darkest) !default;
    $header--dark__input--background-color: style__color(gray) !default;
    $header--dark__input--focus--background-color: style__color(
        gray-light
    ) !default;
    $header--dark__input--border-color: style__color(gray-darkest) !default;
    $header--dark__input--hover--border-color: style__color(black) !default;
    $header--dark__input--focus--border-color: style__color(black) !default;
    $header--dark__input--color: style__color(white) !default;
    $header--dark__submit--border-color: style__color(gray-darkest) !default;
    $header--dark__submit--hover--border-color: style__color(black) !default;
    $header--dark__submit--focus--border-color: style__color(black) !default;
    $header--dark__label--color: style__color(white-darkest) !default;
    
    // Header themes
    $header__themes: (
        white: (
            background-color: $header--white--background-color,
            link-color: $header--white__link--color,
            link-hover-focus-color: $header--white__link--hover-focus--color,
            input-background-color: $header--white__input--background-color,
            input-focus-background-color:
                $header--white__input--focus--background-color,
            input-border-color: $header--white__input--border-color,
            input-hover-border-color: $header--white__input--hover--border-color,
            input-focus-border-color: $header--white__input--focus--border-color,
            input-color: $header--white__input--color,
            submit-border-color: $header--white__submit--border-color,
            submit-hover-border-color: $header--white__submit--hover--border-color,
            submit-focus-border-color: $header--white__submit--focus--border-color,
            label-color: $header--white__label--color
        ),
        gray: (
            background-color: $header--gray--background-color,
            link-color: $header--gray__link--color,
            link-hover-focus-color: $header--gray__link--hover-focus--color,
            input-background-color: $header--gray__input--background-color,
            input-focus-background-color:
                $header--gray__input--focus--background-color,
            input-border-color: $header--gray__input--border-color,
            input-hover-border-color: $header--gray__input--hover--border-color,
            input-focus-border-color: $header--gray__input--focus--border-color,
            input-color: $header--gray__input--color,
            submit-border-color: $header--gray__submit--border-color,
            submit-hover-border-color: $header--gray__submit--hover--border-color,
            submit-focus-border-color: $header--gray__submit--focus--border-color,
            label-color: $header--gray__label--color
        ),
        gold: (
            background-color: $header--gold--background-color,
            link-color: $header--gold__link--color,
            link-hover-focus-color: $header--gold__link--hover-focus--color,
            input-background-color: $header--gold__input--background-color,
            input-focus-background-color:
                $header--gold__input--focus--background-color,
            input-border-color: $header--gold__input--border-color,
            input-hover-border-color: $header--gold__input--hover--border-color,
            input-focus-border-color: $header--gold__input--focus--border-color,
            input-color: $header--gold__input--color,
            submit-border-color: $header--gold__submit--border-color,
            submit-hover-border-color: $header--gold__submit--hover--border-color,
            submit-focus-border-color: $header--gold__submit--focus--border-color,
            label-color: $header--gold__label--color
        ),
        dark: (
            background-color: $header--dark--background-color,
            link-color: $header--dark__link--color,
            link-hover-focus-color: $header--dark__link--hover-focus--color,
            input-background-color: $header--dark__input--background-color,
            input-focus-background-color:
                $header--dark__input--focus--background-color,
            input-border-color: $header--dark__input--border-color,
            input-hover-border-color: $header--dark__input--hover--border-color,
            input-focus-border-color: $header--dark__input--focus--border-color,
            input-color: $header--dark__input--color,
            submit-border-color: $header--dark__submit--border-color,
            submit-hover-border-color: $header--dark__submit--hover--border-color,
            submit-focus-border-color: $header--dark__submit--focus--border-color,
            label-color: $header--dark__label--color
        )
    );
    
    @mixin header__theme($instructions) {
        .cwf-header--#{"" + map-get($instructions, theme)} {
            --cwf-header--background-color: #{map-get(
                    $instructions,
                    background-color
                )};
            --cwf-header__link--color: #{map-get($instructions, link-color)};
            --cwf-header__link--hover-focus--color: #{map-get(
                    $instructions,
                    link-hover-focus-color
                )};
            --cwf-header__input--background-color: #{map-get(
                    $instructions,
                    input-background-color
                )};
            --cwf-header__input--focus--background-color: #{map-get(
                    $instructions,
                    input-focus-background-color
                )};
            --cwf-header__input--border-color: #{map-get(
                    $instructions,
                    input-border-color
                )};
            --cwf-header__input--hover--border-color: #{map-get(
                    $instructions,
                    input-hover-border-color
                )};
            --cwf-header__input--focus--border-color: #{map-get(
                    $instructions,
                    input-focus-border-color
                )};
            --cwf-header__input--color: #{map-get($instructions, input-color)};
            --cwf-header__submit--border-color: #{map-get(
                    $instructions,
                    submit-border-color
                )};
            --cwf-header__submit--hover--border-color: #{map-get(
                    $instructions,
                    submit-hover-border-color
                )};
            --cwf-header__submit--focus--border-color: #{map-get(
                    $instructions,
                    submit-focus-border-color
                )};
            --cwf-header__label--color: #{map-get($instructions, label-color)};
        }
    }
    
    @mixin header__theme--official($theme) {
        $colors: map-get($header__themes, $theme);
    
        $instructions: map-merge(
            $colors,
            (
                theme: $theme
            )
        );
    
        @include header__theme($instructions);
    }
    
    @each $theme, $colors in $header__themes {
        @include header__theme--official($theme);
    }
    
    .cwf-header--compact {
        min-height: 4rem;
        padding-top: 1rem;
        padding-bottom: 1rem;
    }
    
    .cwf-header__container {
        display: flex;
        align-items: center;
        justify-content: center;
        padding-left: 1rem;
        padding-right: 1rem;
        @include theme__contain;
    
        @include media__breakpoint {
            justify-content: space-between;
        }
    }
    
    .cwf-header__title {
        display: flex;
        flex-direction: column;
        align-items: center;
    
        @include media__breakpoint {
            align-items: flex-start;
        }
    }
    
    .cwf-header__title--reverse {
        flex-direction: column-reverse;
    }
    
    .cwf-header__department {
        font-size: 1.1667rem;
        font-weight: 500;
    
        @include media__breakpoint {
            font-size: 2rem;
        }
    }
    
    .cwf-header--compact .cwf-header__department {
        @include media__breakpoint {
            font-size: 1.1667rem;
        }
    }
    
    .cwf-header__department,
    .cwf-header__parent {
        margin-top: 0;
        margin-bottom: 0;
        padding-top: 0;
    }
    
    .cwf-header__parent {
        margin-bottom: 0.5rem;
        font-size: 0.889rem;
        font-weight: 400;
    }
    
    .cwf-header__link {
        font-weight: 400;
        text-decoration: none;
        color: var(--cwf-header__link--color);
        @include animation__transition(color);
    
        &:hover,
        &:focus {
            text-decoration: underline;
            color: var(--cwf-header__link--hover-focus--color);
        }
    }
    
    .cwf-header__link--multi-line {
        display: flex;
        flex-direction: column;
    }
    
    .cwf-header__controls {
        position: absolute;
        top: 0;
        right: 0.25rem;
        display: flex;
        @include style__z-index(page);
    
        @include media__breakpoint {
            display: none;
        }
    }
    
    // Header colors
    $header__toggle--color: darken(
        style__color(gray-dark),
        0.75%
    ) !default; // #313131
    $header__toggle--active--background-color: rgba(
        style__color(black),
        0.05
    ) !default;
    $header__toggle--active--color: style__color(black) !default;
    
    .cwf-header__toggle {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: space-evenly;
        width: 48px;
        height: 64px;
        padding: 0.5rem 0;
        border: none;
        background-color: transparent;
        font-size: 0.65rem;
        font-weight: 700;
        color: var(--cwf-header__toggle--color);
        @include animation__transition(background-color, color);
        --cwf-header__toggle--color: #{$header__toggle--color};
        --cwf-header__toggle--active--background-color: #{$header__toggle--active--background-color};
        --cwf-header__toggle--active--color: #{$header__toggle--active--color};
    
        &:hover,
        &:focus {
            background-color: var(--cwf-header__toggle--active--background-color);
            color: var(--cwf-header__toggle--active--color);
        }
    
        & .cwf-header__icon {
            font-size: 1.25rem;
        }
    }
    
    .cwf-header__features {
        display: none;
    
        @include media__breakpoint {
            display: block;
        }
    }
    
    .cwf-header__exit {
        display: none;
    }
    
    .cwf-header__features--nav-modal {
        align-items: flex-end;
    
        @include media__breakpoint {
            align-items: center;
        }
    }
    
    $header__features--modal--background-color: rgba(
        style__color(black),
        0.75
    ) !default;
    $header__features--modal--reduced-transparency--background-color: style__color(
        black
    ) !default;
    
    .cwf-header__features--nav-modal,
    .cwf-header__features--search-modal {
        position: fixed;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        display: flex;
        justify-content: center;
        padding-left: 1rem;
        padding-right: 1rem;
        background-color: var(--cwf-header__features--modal--background-color);
        @include style__z-index(modal);
    
        --cwf-header__features--modal--background-color: #{$header__features--modal--background-color};
    
        @include media__reduced(transparency) {
            --cwf-header__features--modal--background-color: #{$header__features--modal--reduced-transparency--background-color};
        }
    
        @include media__reduced(transparency, no-preference) {
            --cwf-header__features--modal--background-color: #{$header__features--modal--background-color};
        }
    
        &[aria-hidden="false"] {
            @include animation__animation--fadeIn;
        }
    
        &[aria-hidden="true"] {
            @include animation__animation--fadeOut;
        }
    
        $header__exit--background-color: style__color(black) !default;
        $header__exit--color: style__color(white) !default;
        $header__exit--active--background-color: style__color(black) !default;
        $header__exit--desktop--background-color: rgba(
            style__color(black),
            0.5
        ) !default;
    
        .cwf-header__exit {
            position: absolute;
            top: 0;
            right: 0;
            display: flex;
            align-items: center;
            justify-content: space-evenly;
            min-width: 128px;
            height: 64px;
            padding: 0;
            border: none;
            background-color: var(--cwf-header__exit--background-color);
            font-size: 1rem;
            font-weight: 700;
            color: var(--cwf-header__exit--color);
            @include animation__transition(background-color);
            --cwf-header__exit--background-color: #{$header__exit--background-color};
            --cwf-header__exit--color: #{$header__exit--color};
            --cwf-header__exit--active--background-color: #{$header__exit--active--background-color};
            --cwf-header__exit--desktop--background-color: #{$header__exit--desktop--background-color};
    
            &:hover,
            &:focus {
                background-color: var(--cwf-header__exit--active--background-color);
            }
            &:after {
                content: "\f00d";
                font-family: "Font Awesome 5 Free";
                font-size: 1.5rem;
                font-weight: 900;
            }
    
            @include media__breakpoint {
                background-color: var(
                    --cwf-header__exit--desktop--background-color
                );
            }
        }
    }
    
    .cwf-header__features--search-modal {
        align-items: flex-start;
    
        @include media__breakpoint {
            align-items: center;
        }
    }
    
    .cwf-header__nav {
        display: flex;
        justify-content: flex-end;
        font-size: 0.889rem;
    }
    
    .cwf-header__features--search-modal .cwf-header__nav {
        display: none;
    }
    
    .cwf-header__features--nav-modal .cwf-header__nav {
        display: flex;
        flex-direction: column;
        width: 760px;
        max-width: 760px;
        margin-bottom: 1rem;
    
        @include media__breakpoint {
            margin-bottom: 0;
        }
    }
    
    .cwf-header__features--nav-modal[aria-hidden="false"] .cwf-header__nav {
        @include animation__animation--slideInUp;
    }
    
    .cwf-header__features--nav-modal[aria-hidden="true"] .cwf-header__nav {
        @include animation__animation--slideOutDown;
    }
    
    .cwf-header__nav .cwf-header__link {
        margin-right: 0.5rem;
        text-decoration: underline;
        &:hover,
        &:focus {
            text-decoration: none !important;
        }
        &:last-child {
            margin-right: 0;
        }
    }
    
    $header__features--nav-modal__link--border-color: style__color(
        white-darkest
    ) !default;
    $header__features--nav-modal__link--background-color: style__color(
        white
    ) !default;
    $header__features--nav-modal__link--color: style__color(gray-darkest) !default;
    $header__features--nav-modal__link--active--border-color: rgba(
        style__color(black),
        0.125
    ) !default;
    $header__features--nav-modal__link--active--background-color: theme__accent--background(
    
    ) !default;
    $header__features--nav-modal__link--active--color: theme__accent--foreground(
    
    ) !default;
    
    .cwf-header__features--nav-modal .cwf-header__link {
        display: block;
        width: 100%;
        max-width: 760px;
        margin-right: 0;
        padding: 1rem 1.5rem;
        border-bottom: 2px solid
            var(--cwf-header__features--nav-modal__link--border-color);
        background-color: var(
            --cwf-header__features--nav-modal__link--background-color
        );
        font-size: 1.5rem;
        text-decoration: none;
        color: var(--cwf-header__features--nav-modal__link--color);
        transition: none;
        --cwf-header__features--nav-modal__link--border-color: #{$header__features--nav-modal__link--border-color};
        --cwf-header__features--nav-modal__link--background-color: #{$header__features--nav-modal__link--background-color};
        --cwf-header__features--nav-modal__link--color: #{$header__features--nav-modal__link--color};
        --cwf-header__features--nav-modal__link--active--border-color: #{$header__features--nav-modal__link--active--border-color};
        --cwf-header__features--nav-modal__link--active--background-color: #{$header__features--nav-modal__link--active--background-color};
        --cwf-header__features--nav-modal__link--active--color: #{$header__features--nav-modal__link--active--color};
    
        &:hover,
        &:focus {
            border-color: var(
                --cwf-header__features--nav-modal__link--active--border-color
            );
            background-color: var(
                --cwf-header__features--nav-modal__link--active--background-color
            );
            color: var(--cwf-header__features--nav-modal__link--active--color);
        }
    
        &:focus {
            outline: none;
        }
    
        &:first-of-type {
            border-top-left-radius: 0.5rem;
            border-top-right-radius: 0.5rem;
        }
    
        &:last-of-type {
            border-bottom: none;
            border-bottom-right-radius: 0.5rem;
            border-bottom-left-radius: 0.5rem;
        }
    }
    
    .cwf-header__nav ~ .cwf-header__search {
        margin-top: 0.5rem;
    }
    
    .cwf-header__search {
        position: relative;
        display: flex;
        flex: 1;
    }
    
    .cwf-header__features--search-modal .cwf-header__nav ~ .cwf-header__search {
        margin-top: calc(64px + 1rem);
    
        @include media__breakpoint {
            margin-top: 0;
        }
    }
    
    .cwf-header__features--nav-modal .cwf-header__search {
        display: none;
    }
    
    .cwf-header__features--search-modal .cwf-header__search {
        display: flex;
        flex-direction: column;
        width: 100%;
        min-width: 0;
        max-width: 760px;
    }
    
    .cwf-header__features--search-modal[aria-hidden="false"] .cwf-header__search {
        @include animation__animation--slideInUp;
    }
    
    .cwf-header__features--search-modal[aria-hidden="true"] .cwf-header__search {
        @include animation__animation--slideOutDown;
    }
    
    .cwf-header__input {
        flex: 1;
        font-size: 0.889rem;
        padding: 0.5rem 2.25rem 0.5rem 0.75rem;
        border-width: 1px;
        border-style: solid;
        border-radius: 1.5rem;
        border-color: var(--cwf-header__input--border-color);
        background-color: var(--cwf-header__input--background-color);
        color: var(--cwf-header__input--color);
        @include animation__transition(border-color, background-color);
    
        &:hover {
            border-color: var(--cwf-header__input--hover--border-color);
        }
    
        &:focus {
            background-color: var(--cwf-header__input--focus--background-color);
        }
    }
    
    .cwf-header__input:focus,
    .cwf-header__input:focus:hover {
        outline: none;
        border-color: var(--cwf-header__input--focus--border-color);
    }
    
    $header__features--search-modal__input--border-color: style__color(
        white
    ) !default;
    $header__features--search-modal__input--background-color: style__color(
        white
    ) !default;
    $header__features--search-modal__input--color: style__color(
        gray-darkest
    ) !default;
    $header__features--search-modal__input--active--border-color: style__color(
        gray-darkest
    ) !default;
    
    .cwf-header__features--search-modal .cwf-header__input {
        font-size: 1.5rem;
        padding: 1rem 4rem 1rem 1.5rem;
        border-width: 2px;
        border-color: var(
            --cwf-header__features--search-modal__input--border-color
        );
        border-radius: 3rem;
        background-color: var(
            --cwf-header__features--search-modal__input--background-color
        );
        color: var(--cwf-header__features--search-modal__input--color);
        transition: none;
        --cwf-header__features--search-modal__input--border-color: #{$header__features--search-modal__input--border-color};
        --cwf-header__features--search-modal__input--background-color: #{$header__features--search-modal__input--background-color};
        --cwf-header__features--search-modal__input--color: #{$header__features--search-modal__input--color};
        --cwf-header__features--search-modal__input--active--border-color: #{$header__features--search-modal__input--active--border-color};
    
        &:focus,
        &:focus:hover {
            border-color: var(
                --cwf-header__features--search-modal__input--active--border-color
            );
        }
    }
    
    .cwf-header__submit {
        position: absolute;
        right: 0.4rem;
        top: 50%;
        display: flex;
        justify-content: center;
        align-items: center;
        width: 1.5rem;
        height: 1.5rem;
        border: 1px solid var(--cwf-header__submit--border-color);
        border-radius: 100%;
        background-color: var(--cwf-header__input--background-color);
        font-size: 0.6rem;
        color: var(--cwf-header__label--color);
        transform: translateY(-50%);
        @include animation__transition(border-color, background-color, color);
    
        &:hover {
            border-color: var(--cwf-header__submit--hover--border-color);
        }
    
        &:focus {
            outline: none;
            border-color: var(--cwf-header__submit--focus--border-color);
            background-color: var(--cwf-header__input--focus--background-color);
            color: var(--cwf-header__input--color);
        }
    }
    
    $header__features--search-modal__submit--border-color: style__color(
        white-darkest
    ) !default;
    $header__features--search-modal__submit--background-color: style__color(
        white
    ) !default;
    $header__features--search-modal__submit--color: style__color(
        gray-lightest
    ) !default;
    $header__features--search-modal__submit--hover--border-color: style__color(
        gray-lightest
    ) !default;
    $header__features--search-modal__submit--focus--border-color: style__color(
        gray-darkest
    ) !default;
    $header__features--search-modal__submit--focus--color: style__color(
        gray-darkest
    ) !default;
    
    .cwf-header__features--search-modal .cwf-header__submit {
        right: 0.5rem;
        width: 3rem;
        height: 3rem;
        border-width: 2px;
        border-color: var(
            --cwf-header__features--search-modal__submit--border-color
        );
        background-color: var(
            --cwf-header__features--search-modal__submit--background-color
        );
        font-size: 1rem;
        color: var(--cwf-header__features--search-modal__submit--color);
        transition: none;
        --cwf-header__features--search-modal__submit--border-color: #{$header__features--search-modal__submit--border-color};
        --cwf-header__features--search-modal__submit--background-color: #{$header__features--search-modal__submit--background-color};
        --cwf-header__features--search-modal__submit--color: #{$header__features--search-modal__submit--color};
        --cwf-header__features--search-modal__submit--hover--border-color: #{$header__features--search-modal__submit--hover--border-color};
        --cwf-header__features--search-modal__submit--focus--border-color: #{$header__features--search-modal__submit--focus--border-color};
        --cwf-header__features--search-modal__submit--focus--color: #{$header__features--search-modal__submit--focus--color};
    
        &:hover {
            border-color: var(
                --cwf-header__features--search-modal__submit--hover--border-color
            );
        }
    
        &:focus {
            border-color: var(
                --cwf-header__features--search-modal__submit--focus--border-color
            );
            color: var(--cwf-header__features--search-modal__submit--focus--color);
        }
    }
    
    .cwf-header__label {
        position: absolute;
        left: 0.75rem;
        font-size: 0.889rem;
        color: var(--cwf-header__label--color);
        transform: translateY(-50%);
        @include animation__animation--slideFadeIn(forwards);
    
        &:hover {
            cursor: text;
        }
    }
    
    .cwf-header__input:valid ~ .cwf-header__label {
        @include animation__animation--slideFadeOut(forwards);
    }
    
    $header__features--search-modal__label--color: style__color(
        gray-lightest
    ) !default;
    
    .cwf-header__features--search-modal .cwf-header__label {
        left: 1.5rem;
        font-size: 1.5rem;
        color: var(--cwf-header__features--search-modal__label--color);
        --cwf-header__features--search-modal__label--color: #{$header__features--search-modal__label--color};
    }
    
  • URL: /components/raw/header/_index.scss
  • Filesystem Path: components/header/_index.scss
  • Size: 26.4 KB
  • Content:
    // The default component class
    import { Component } from '../../shared/component';
    
    // Lock/unlock document scrolling
    import { toggleScrollLock } from '../../shared/event';
    
    // Provide functionality to the header
    export class Header extends Component {
        constructor({
            header = 'cwf-header',
            features = 'cwf-header__features',
            featuresNavModal = 'cwf-header__features--nav-modal',
            featuresSearchModal = 'cwf-header__features--search-modal',
            toggle = 'cwf-header__toggle',
            nav = 'cwf-header__nav',
            search = 'cwf-header__search',
            link = 'cwf-header__link',
            input = 'cwf-header__input',
            submit = 'cwf-header__submit',
            preventScroll = 'cwf--prevent-scroll',
            exit = 'cwf-header__exit'
        } = {}) {
            super();
    
            // Store the selectors,...
            this.selectors = {
                header,
                features,
                featuresNavModal,
                featuresSearchModal,
                toggle,
                nav,
                search,
                link,
                input,
                submit,
                preventScroll,
                exit
            };
            // ... initialize a features, listener, and modal state variables,...
            this.features = {};
            this.feature = {};
            this.listener = {};
            this.modal = {};
            // ... and bind "this" to all methods that need it
            this.blurFeaturesModal = this.blurFeaturesModal.bind(this);
            this.modalOnClick = this.modalOnClick.bind(this);
            this.modalOnKeyDown = this.modalOnKeyDown.bind(this);
            this.closeFeaturesModal = this.closeFeaturesModal.bind(this);
            this.focusFeature = this.focusFeature.bind(this);
            this.navLinksOnKeyDown = this.navLinksOnKeyDown.bind(this);
            this.navLinksOnFocusBlur = this.navLinksOnFocusBlur.bind(this);
            this.onKeyDown = this.onKeyDown.bind(this);
    
            // Register the toggle scroll lock function to this component
            this.toggleScrollLock = toggleScrollLock.bind(this);
        }
    
        // Stop showing the features as a modal
        blurFeaturesModal() {
            // Remove the event listener that triggered this function,...
            this.features.wrapper[this.listener.modal](
                'animationend',
                this.blurFeaturesModal,
                true
            );
            // ... remove the modal classes from the feature wrapper,...
            this.features.wrapper.className = this.selectors.features;
            // ... remove the aria-hidden attribute from the feature wrapper,...
            this.features.wrapper.removeAttribute('aria-hidden');
            // ... toggle the scroll lock,...
            this.toggleScrollLock();
            // ... and reinitialize the global modal object
            this.modal = {};
        }
    
        // Close the features modal
        closeFeaturesModal() {
            // Set the modal to be closed...
            this.modal.open = false;
    
            // Set the features to be hidden...
            this.features.wrapper.setAttribute('aria-hidden', true);
            // ... and stop showing the features as a modal when its animation ends
            this.features.wrapper[this.listener.modal](
                'animationend',
                this.blurFeaturesModal,
                true
            );
    
            // Unfocus the search input
            document.activeElement.blur();
    
            // Set all modal event listeners to be removed
            this.listener.modal = 'removeEventListener';
    
            // Remove the event listeners for the exit functionality
            this.features.wrapper[this.listener.modal](
                'click',
                this.modalOnClick,
                true
            );
            this.features.exit[this.listener.modal](
                'click',
                this.modalOnClick,
                true
            );
            document[this.listener.modal]('keydown', this.modalOnKeyDown, true);
        }
    
        // Trap the focus
        trapFocus(previous, focusable, event) {
            // Store the first/last fosuable feature element and its index,...
            const firstIndex = 0,
                firstFocusable = focusable[firstIndex],
                lastIndex = focusable.length - 1,
                lastFocusable = focusable[lastIndex],
                // ... the current focused item,...
                focused = focusable.indexOf(document.activeElement),
                // ... and the trigger and element to wrap focus to
                trigger = previous ? focused === firstIndex : focused == lastIndex,
                wrapElement = previous ? lastFocusable : firstFocusable;
    
            // If an event was provided, prevent its default behavior
            if (event) event.preventDefault();
    
            // If focus trapping hasn't been triggered,...
            if (!trigger) {
                // ... find the index of the element to focus...
                const index = previous ? focused - 1 : focused + 1;
                // ... and focus it
                return focusable[index].focus();
            }
    
            // Otherwise, focus the element to wrap around to...
            wrapElement.focus();
        }
    
        // Bind to the features' wrapper and exit button's click event when in modal view
        modalOnClick({ target, currentTarget }) {
            // Check to see if the target is the features wrapper...
            const targetIsFeatures = target === this.features.wrapper,
                // ... and the current target is the exit button
                currentTargetIsExit = currentTarget === this.features.exit;
    
            // If the target is the features or the current target is the exit button, close the features modal
            if (targetIsFeatures || currentTargetIsExit)
                return this.closeFeaturesModal();
        }
    
        // Bind to the document's key-down event when in modal view
        modalOnKeyDown(event) {
            const { key, shiftKey } = event;
    
            // If "Escape" is pressed, close the features modal
            if (key === 'Escape') return this.closeFeaturesModal();
    
            // If "Tab" is pressed, trap the focus
            if (key === 'Tab')
                return this.trapFocus(shiftKey, this.feature.focusable, event);
        }
    
        // Open the features wrapper as a modal
        openFeaturesAsModal() {
            // Set the modal to be opened...
            this.modal.open = true;
            // ... and all modal event listener's to be added
            this.listener.modal = 'addEventListener';
    
            // Prevent the body from scrolling
            this.toggleScrollLock();
    
            // Show the features wrapper as a modal...
            this.features.wrapper.classList.add(this.modal.class);
            // ... and set it to be visible in order to animate
            this.features.wrapper.setAttribute('aria-hidden', false);
    
            // Bind to the features' wrapper and exit button's click event...
            this.features.wrapper[this.listener.modal](
                'click',
                this.modalOnClick,
                true
            );
            this.features.exit[this.listener.modal](
                'click',
                this.modalOnClick,
                true
            );
            // ... and the document's key-down event
            document[this.listener.modal]('keydown', this.modalOnKeyDown, true);
        }
    
        // Focus a feature's first element of a given selector
        focusFeatureElement() {
            // Attempt to grab the feature element from the given selector...
            const element = this.feature.element.querySelector(
                `.${this.feature.focus}`
            );
            // ... and if doesn't exist, do nothing else
            if (!element) return;
    
            // Otherwise, focus the element
            element.focus();
        }
    
        // Checks to see if the feature is within the viewport
        featureIsWithinViewport() {
            // If the feature element has not been set, do nothing,...
            if (!this.feature.element) return;
            // ... otherwise, check to see if the feature is within the viewport...
            const scroll = window.scrollY || window.pageYOffset,
                boundsTop =
                    this.feature.element.getBoundingClientRect().top + scroll,
                viewport = {
                    top: scroll,
                    bottom: scroll + window.innerHeight
                },
                bounds = {
                    top: boundsTop,
                    bottom: boundsTop + this.feature.element.clientHeight
                };
            // ... and return the findings
            return (
                (bounds.bottom >= viewport.top &&
                    bounds.bottom <= viewport.bottom) ||
                (bounds.top <= viewport.bottom && bounds.top >= viewport.top)
            );
        }
    
        // Checks to see if the feature is visible (e.g. not display: none;)
        featureIsVisible() {
            // If the feature element has not been set, do nothing,...
            if (!this.feature.element) return;
            // ... otherwise, return whether the feature is visible (e.g. not display: none;)
            return this.feature.element.offsetParent !== null;
        }
    
        // Select an argument based off the feature's type
        selectFromFeatureType(navOption, searchOption) {
            // If the feature element has not been set, do nothing
            if (!this.feature.element) return;
    
            // If the feature is a nav, return the nav argument
            if (
                this.features.nav &&
                this.feature.element === this.features.nav.element
            ) {
                return typeof navOption === 'function' ? navOption() : navOption;
            }
    
            // If the feature is a search, return the search argument
            if (
                this.features.search &&
                this.feature.element === this.features.search.element
            ) {
                return typeof searchOption === 'function'
                    ? searchOption()
                    : searchOption;
            }
        }
    
        // Return all focusable nav elements
        returnFocusableNavElements() {
            return [
                this.features.exit, // Exit button
                ...this.features.nav.links // Nav links
            ];
        }
    
        // Return all focusable search elements
        returnFocusableSearchElements() {
            return [
                this.features.exit, // Exit button
                this.features.search.input, // Search input
                this.features.search.submit // Search submit button
            ];
        }
    
        // Focus a header feature
        focusFeature({ currentTarget }) {
            // Attempt to grab the header feature...
            const id = currentTarget.getAttribute('aria-controls');
            this.feature.element = document.getElementById(id);
            // ... and if it doesn't exist, do nothing else
            if (!this.feature.element) return;
    
            // Figure out what element to focus and modal class to use based off its type
            this.feature.focus = this.selectFromFeatureType(
                this.selectors.link,
                this.selectors.input
            );
    
            // If the modal is already open, close it
            if (this.modal.open) return this.closeFeaturesModal();
    
            // If the feature is not within the viewport or is not visible, open the features as a modal
            if (!this.featureIsVisible() || !this.featureIsWithinViewport()) {
                // Find what elements are focusable within the feature...
                this.feature.focusable = this.selectFromFeatureType(
                    this.returnFocusableNavElements.bind(this),
                    this.returnFocusableSearchElements.bind(this)
                );
                // ... and modal class to be used,...
                this.modal.class = this.selectFromFeatureType(
                    this.selectors.featuresNavModal,
                    this.selectors.featuresSearchModal
                );
                // ... and open the features wrapper as a modal
                this.openFeaturesAsModal();
            }
    
            // Focus the feature's first element of the given selector
            this.focusFeatureElement();
        }
    
        // Bind to every toggle's click event
        togglesOnClick() {
            // If no toggles exist, do nothing else
            if (!this.toggles.length) return;
    
            // For each toggle,...
            this.toggles.forEach((toggle) => {
                // ... open the features as a modal when clicked
                toggle[this.listener.global]('click', this.focusFeature);
            });
        }
    
        // Bind to every nav link's key-down event if focused
        navLinksOnKeyDown(event) {
            // Grab the type, current target, and key from the event
            const { type, currentTarget, key } = event;
    
            // If the type is focus/blur...
            if (['focus', 'blur'].includes(type)) {
                // ... add/remove key-down functionality respectively
                const listener =
                    type === 'focus' ? 'addEventListener' : 'removeEventListener';
                return currentTarget[listener]('keydown', this.navLinksOnKeyDown);
            }
    
            // Always assign Home/End keys for navigation,...
            const navigation = ['Home', 'End'];
            // ... figure out what previous/next keys make sense based on if a modal is open,...
            const previous = this.modal.open ? 'ArrowUp' : 'ArrowLeft',
                next = this.modal.open ? 'ArrowDown' : 'ArrowRight';
            // ... and push them to the navigation array
            navigation.push(previous, next);
    
            // If the current key pressed is not within the navigation, do nothing else
            if (!navigation.includes(key)) return;
    
            // Otherwise, prevent the event's default behavior...
            event.preventDefault();
            // ... and handle the navigation
            switch (key) {
                case 'Home':
                    // Home = Focus the first nav link
                    return this.features.nav.links[0].focus();
                case 'End':
                    // End = Focus the last nav link
                    return this.features.nav.links[
                        this.features.nav.links.length - 1
                    ].focus();
                default:
                    // Arrow keys = trap focus within the nav links
                    return this.trapFocus(
                        key === previous,
                        this.features.nav.links
                    );
            }
        }
    
        // Bind to every nav link's focus/blur event
        navLinksOnFocusBlur() {
            // If a nav...
            if (!this.features.nav) return;
            // ... or nav links exist, do nothing else
            if (!this.features.nav.links) return;
    
            // For each nav link...
            this.features.nav.links.forEach((link) => {
                // ... bind to its key-down even when focused
                link[this.listener.global]('focus', this.navLinksOnKeyDown);
                link[this.listener.global]('blur', this.navLinksOnKeyDown);
            });
        }
    
        // Bind to the document's key presses
        onKeyDown(event) {
            // Check to see if the user is typing within a text area or input...
            const focused = document.activeElement,
                inputting = ['TEXTAREA', 'INPUT'].includes(focused.tagName);
            // ... and if so, do nothing else
            if (inputting) return;
    
            // Grab the key pressed from the event
            const { key } = event;
    
            // Attempt to find a toggle with a key shorcut that matches the one pressed...
            const toggle = this.toggles.find((toggle) => {
                return toggle.getAttribute('aria-keyshortcuts') === key;
            });
            // ... and if doesn't exist, do nothing else
            if (!toggle) return;
    
            // Prevent the default event behavior...
            event.preventDefault();
            // ... and virtually click the toggle
            toggle.click();
        }
    
        // Bind/unbind to the document's key-down event
        documentOnKeyDown() {
            document[this.listener.global]('keydown', this.onKeyDown);
        }
    
        // Destroy the header functionality
        destroy() {
            // Ensure all listeners are removed,...
            this.listener.global = 'removeEventListener';
            // ... unbind from every toggle's click event,...
            this.togglesOnClick();
            // ... unbind from the document's key-down event,...
            this.documentOnKeyDown();
            // ... and reset all state variables
            this.features = {};
            this.feature = {};
            this.listener = {};
            this.modal = {};
        }
    
        // Get the nav
        getNav() {
            // Attempt to grab the nav...
            const nav = this.features.wrapper.querySelector(
                `.${this.selectors.nav}`
            );
            // ... and if none exists, do nothing else
            if (!nav) return;
    
            // Otherwise, store it and its relevant children
            this.features.nav = {};
            this.features.nav.element = nav;
            this.features.nav.links = Array.from(
                nav.querySelectorAll(`.${this.selectors.link}`)
            );
        }
    
        // Get the search
        getSearch() {
            // Attempt to grab the search...
            const search = this.features.wrapper.querySelector(
                `.${this.selectors.search}`
            );
            // ... and if none exists, do nothing else
            if (!search) return;
    
            // Otherwise, store it and its relevant children
            this.features.search = {};
            this.features.search.element = search;
            this.features.search.input = search.querySelector(
                `.${this.selectors.input}`
            );
            this.features.search.submit = search.querySelector(
                `.${this.selectors.submit}`
            );
        }
    
        // Initialize the header functionality
        initialize() {
            // Attempt to find the header...
            this.header = document.querySelector(`.${this.selectors.header}`);
            // ... and if it doesn't exist, do nothing
            if (!this.header) return;
    
            // Attempt to find the header features...
            this.features.wrapper = this.header.querySelector(
                `.${this.selectors.features}`
            );
            // ... and if doesn't exist, do nothing
            if (!this.features.wrapper) return;
    
            // Grab all toggles...
            this.toggles = Array.from(
                this.header.querySelectorAll(`.${this.selectors.toggle}`)
            );
            // ... and the feature's exit button
            this.features.exit = this.features.wrapper.querySelector(
                `.${this.selectors.exit}`
            );
    
            // Attempt to grab the nav...
            this.getNav();
            // ... and search features
            this.getSearch();
    
            // Ensure all global listeners are added
            this.listener.global = 'addEventListener';
    
            // Bind to every toggle's click event,...
            this.togglesOnClick();
            // ... every nav link's focus/blur events,...
            this.navLinksOnFocusBlur();
            // ... and the document's key-down event
            this.documentOnKeyDown();
        }
    }
    
    export default Header;
    
  • URL: /components/raw/header/index.js
  • Filesystem Path: components/header/index.js
  • Size: 18.4 KB

Header

The header component should be used at the top of every page to show the title of the website and provide a link to the homepage. The header also includes a few optional elements such a link to a unit’s parent unit, a search form and place for quick links.

Variations

The header component has the ability to have a white, gray, gold or dark background. By default the background will be white.

The header component also has the option to display in compact mode, which will reduce the font size of the unit name and the padding size of the wrapping block (while at a md breakpoint or larger). When using the header in compact mode it is suggested to not include the optional parent unit information. Consider using compact mode for sites or applications with internal audiences.

T4 implementation

When using the header content type in T4 the component will show in it’s default size. Consider using name injectors to force the compact version.