# `hb-layout-mobile`

**Category:** layout · **Tags:** layout, shell, responsive · **Package:** `@htmlbricks/hb-layout-mobile`

## Overview

`hb-layout-mobile` is a mobile-first application shell: optional offcanvas navigation (`hb-offcanvas` + `hb-sidebar-desktop`), a top `hb-navbar`, a primary `page` slot, an optional `hb-cookie-law-banner`, and `hb-footer`. It forwards navbar, footer, and navigation-related custom events to the host.

The host element is a fixed-height viewport column (`100vh` / `100dvh`, `overflow: hidden`). The inner `.hb-layout-mobile__shell` is a flex column that stacks navbar, scrollable page region, and—depending on mode—cookie banner and footer.

### Scroll modes

- **Default (`single_screen` off or `"no"`):** The main band (part `page`) scrolls vertically. Inside it, the slot sits in a flex column with the cookie banner and footer so the slot grows when content is short, keeping the footer at the bottom of the scroll area (see `.hb-layout-mobile__page--footer-inside` in styles). When `footer.type` is `"auto"`, the embedded footer uses the **`regular`** size.
- **`single_screen` on (any string except `"no"`, or boolean true):** Cookie banner and footer sit **outside** the scroll area at the bottom of the shell; **only** the page slot scrolls. When `footer.type` is `"auto"`, the footer uses the **`small`** size.

If you embed the host in a sized container instead of the full viewport, give ancestors a defined height and set `height: 100%` on `hb-layout-mobile` so the flex chain resolves.

## Dependencies

The bundle registers nested packages (see `extra/docs.ts`): `hb-footer` (with `hb-contact-item`), `hb-offcanvas` (with `hb-sidenav-link`, `hb-sidebar-desktop`), `hb-navbar` (with `hb-dropdown-simple`), `hb-cookie-law-banner`, and `hb-sidebar-desktop`. Nested components ship their own Bulma scopes; theme tokens on the document still apply where shared.

## Styling

### Host and layout (SCSS)

`styles/webcomponent.scss` pins `:host` to full dynamic viewport height, hides overflow, and uses flex for the shell. The page area uses `overflow-y: auto` where scrolling applies. Bootstrap Icons are loaded for nested UI. `styles/bulma.scss` forwards Bulma flex helpers and host-scoped theme tokens so the shell can align with the Bulma variable system.

### Theme variables (`--bulma-*`)

Set public Bulma CSS variables on `:root` / `body` (or any ancestor) so nested shadow roots that read them stay on-brand. Documented in `extra/docs.ts`:

| Variable | Role |
|----------|------|
| `--bulma-scheme-main` | Default surfaces inside nested chrome. |
| `--bulma-text` | Primary text in navbar and footer. |
| `--bulma-navbar-background-color` | Top bar background (forwarded to `hb-navbar`). |
| `--bulma-navbar-item-color` | Navbar links and controls. |
| `--bulma-footer-background-color` | Footer background when `hb-footer` is used. |
| `--bulma-link` | Interactive accents (dropdowns, links). |

See [Bulma CSS variables](https://bulma.io/documentation/features/css-variables/).

### CSS parts

| Part | Description |
|------|-------------|
| `container` | Outer flex column stacking navbar, page, and (in single-screen mode) fixed cookie/footer. |
| `navbar` | Wrapper around `hb-navbar` for shell alignment. |
| `page` | Main column: scrollable viewport for content (and in default mode, cookie + footer inside the same scroll stack). |
| `footer` | Wrapper around the embedded `hb-footer`. |

### HTML slots

| Slot | Description |
|------|-------------|
| `nav-header-slot` | Rendered in `hb-offcanvas` header span (only when `navlinks` is non-empty). |
| `nav-left-slot` | Forwarded to `hb-navbar` `left-slot`. |
| `nav-center-slot` | Forwarded to `hb-navbar` `center-slot`. |
| `nav-right-slot` | Forwarded to `hb-navbar` `right-slot`. |
| `page` | Primary content between navbar and footer/cookie stack. |

## Custom element

`hb-layout-mobile`

## Attributes (HTML)

Web component attributes use **snake_case** and **string** values. Objects and arrays must be **JSON strings**. Booleans from HTML are typically `"yes"` / `"no"` (this repo’s convention); some props also accept `"true"` / `"false"` where noted.

| Attribute | Description |
|-----------|-------------|
| `id` | Optional host identifier (typed on `Component`). |
| `style` | Optional inline style string (typed on `Component`). |
| `company` | JSON: company / brand payload for navbar and footer (`ICompany`). |
| `contacts` | JSON: footer contacts (`IContacts`). |
| `socials` | JSON: footer social links (`ISocials`). |
| `navlinks` | JSON: `INavLink[]`. When the array is **non-empty**, `hb-offcanvas` is rendered and the navbar shows the menu control; when empty or omitted, offcanvas is omitted and `noburger` is set on the navbar. |
| `sidebar` | JSON: offcanvas chrome — `title`, `logo`, `type`, optional `enablefooter`, `enablethemeswitch` (`"yes"` / `"no"` / `"false"` / empty). Passed through to `hb-offcanvas` / sidebar where used. |
| `usermenu` | JSON: `IUserMenu` for the navbar avatar dropdown. |
| `pagename` | Active navigation key for offcanvas (`navpage` on `hb-offcanvas`). |
| `page_title` | Navbar brand text (overrides `company.siteName` when set). |
| `footer` | JSON: `{ type?: "auto" \| "small" \| "regular" \| "large"; disable_expanding_small?: boolean }`. Defaults to `{ type: "auto", disable_expanding_small: false }` when coerced from string. |
| `columns` | JSON: `IColumn[]` for `hb-footer`. |
| `policies` | JSON: `IPolicies[]` for `hb-footer`. |
| `cookielaw` | `"yes"` / `"true"` (and other typed literals) — part of cookie banner visibility. |
| `cookielawuri4more` | Policy link URI string for the cookie banner. |
| `cookielawlanguage` | Language string for the cookie banner. |
| `cookielawallowdecline` | `"yes"` / `"true"` / `"no"` / `"false"` — banner decline behavior. |
| `single_screen` | String `"no"` disables single-screen mode; any other non-empty string enables it (coerced to boolean). Controls fixed footer/cookie vs scrolling layout (see above). |
| `i18nlang` | Language code passed into nested widgets (e.g. `en`, `it`). |
| `i18nlanguages` | JSON array `{ code, label }[]` or string — language options for the offcanvas/sidebar path. |

### Cookie banner visibility

`hb-cookie-law-banner` is rendered when **any** of the following holds: `cookielaw` is `"yes"` or `"true"`, `cookielawallowdecline` is `"yes"` or `"no"`, `cookielawlanguage` is a non-empty string, or `cookielawuri4more` is a non-empty string. The banner receives `language` from `cookielawlanguage` or `i18nlang`, plus `allowdecline` and `cookielawuri4more`.

## Events

Custom events bubble from the host as declared in `types/webcomponent.type.d.ts`:

| Event | `detail` |
|-------|----------|
| `offcanvasswitch` | `{ isOpen: boolean }` — from offcanvas or navbar menu switch. |
| `pageChange` | `{ page: string }` — offcanvas page selection. |
| `navbarDropDownClick` | `{ key: string }` — navbar dropdown entry. |
| `footerClick` | `{ elClick: string }` — footer interaction key. |
| `navbarSlotClick` | `{ side: "left" \| "right" \| "center" }` — navbar slot area click. |
| `languageChange` | Same shape as `hb-sidebar-desktop` `languageChange` (e.g. `{ code: string }`). |
| `themeChange` | Same shape as `hb-sidebar-desktop` `themeChange` (theme preference). |

## Usage notes

- **Navigation icons:** `navlinks` entries use Bootstrap Icons names **without** the `bi-` prefix (e.g. `house-door`), consistent with other layout/nav components.
- **JSON props:** At runtime, `footer`, `sidebar`, and `company` may be parsed from JSON strings in effects when passed as strings from attributes.

## TypeScript (`types/webcomponent.type.d.ts`)

```typescript
import type {
  IContacts,
  ISocials,
  ICompany,
  IColumn,
  IPolicies,
} from "../../footer/types/webcomponent.type";
import type { IUserMenu } from "../../navbar/types/webcomponent.type";
import type { INavLink } from "../../sidenav-link/types/webcomponent.type";
import type { Events as SidebarDesktopEvents } from "../../sidebar-desktop/types/webcomponent.type";

export type I18nLanguageOption = { code: string; label: string };

export type Component = {
  id?: string;
  style?: string;
  socials?: ISocials;
  contacts?: IContacts;
  company?: ICompany;
  navlinks?: INavLink[];
  pagename?: string;
  page_title?: string;
  usermenu?: IUserMenu;
  cookielaw?: "yes" | "true" | "no" | "false" | null | "" | undefined;
  columns?: IColumn[];
  single_screen?: boolean;
  cookielawuri4more?: string;
  cookielawallowdecline?: "yes" | "true" | "no" | "false" | null | "" | undefined;
  cookielawlanguage?: string;
  sidebar?: {
    title?: string;
    logo?: string;
    type?: string;
    enablefooter?: "yes" | "no" | "false" | null | "" | undefined;
    enablethemeswitch?: "yes" | "no" | "false" | null | "" | undefined;
  };
  footer?: {
    type?: "auto" | "small" | "regular" | "large";
    disable_expanding_small?: boolean;
  };
  policies?: IPolicies[];
  i18nlang?: string;
  i18nlanguages?: I18nLanguageOption[] | string;
};

export type Events = {
  offcanvasswitch: { isOpen: boolean };
  pageChange: { page: string };
  navbarDropDownClick: { key: string };
  footerClick: { elClick: string };
  navbarSlotClick: { side: "left" | "right" | "center" };
  languageChange: SidebarDesktopEvents["languageChange"];
  themeChange: SidebarDesktopEvents["themeChange"];
};
```

## Minimal HTML example

```html
<hb-layout-mobile i18nlang="en"></hb-layout-mobile>
```
