npm package: @shaman-apprentice/page-skeleton
Description: A custom element web component providing slots for header, sidebar and content.
Author: shaman-apprentice
Latest version: 1.0.1

# page-skeleton

A custom element web component providing slots for header, sidebar and content. Works with any framework (Angular, React, Vue, etc.) or vanilla JavaScript.

![example image](https://github.com/shaman-apprentice/page-skeleton/blob/HEAD/lib/docs/demo.excalidraw.png)

## Installation

```bash
npm install @shaman-apprentice/page-skeleton
```

**Note:** Make sure your CSS reset sets `body { margin: 0; }` for the component to display correctly. Most modern CSS resets include this anyway.

## Usage

### With Angular

```typescript
import { Component, signal, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@shaman-apprentice/page-skeleton';

@Component({
  selector: 'app-root',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <page-skeleton 
      [attr.sidebar-open]="isSidebarOpen() ? '' : null"
      [attr.sidebar-width-in-px]="sidebarWidth()"
      (sidebar-width-changed)="onSidebarWidthChanged($event)"
      animated
    >
      <header slot="header">
        <button (click)="isSidebarOpen.set(!isSidebarOpen())">Toggle</button>
        My App
      </header>
      
      <aside slot="sidebar">Sidebar content</aside>
      
      <section slot="sidebar-border"></section>
      
      <main slot="content">Main content</main>
    </page-skeleton>
  `
})
export class AppComponent {
  isSidebarOpen = signal(true);
  sidebarWidth = signal(320);
  
  onSidebarWidthChanged(event: CustomEvent<{ width: number }>) {
    this.sidebarWidth.set(event.detail.width);
  }
}
```

## API

### Slots

| Slot Name | Description |
| --- | --- |
| `header` | Fixed header at the top |
| `sidebar` | Collapsible sidebar on the left |
| `sidebar-border` | Draggable border between sidebar and content |
| `content` | Main content area |

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `sidebar-width-in-px` | `number` | `320` | Current sidebar width in pixels |
| `sidebar-min-width-in-px` | `number` | `240` | Minimum sidebar width in pixels |
| `sidebar-open` | `boolean` | `false` | Whether sidebar is open |
| `disable-dragging` | `boolean` | `false` | Disable sidebar resize dragging |
| `animated` | `boolean` | `false` | Enable slide animation for sidebar |

### Properties (JavaScript)

Access via JavaScript:

```javascript
const skeleton = document.querySelector('page-skeleton');
skeleton.sidebarWidthInPx = 400;
skeleton.sidebarOpen = true;
```

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `sidebar-width-changed` | `{ width: number }` | Fired when sidebar is resized by dragging |

### CSS Variables

| Variable | Default | Description |
| --- | --- | --- |
| `--page-skeleton_header-height` | `48px` | Header height |
| `--page-skeleton_sidebar_border-width` | `1px` | Border width |
| `--page-skeleton_transition-duration` | `0.3s` | Animation duration |
| `--page-skeleton_transition-timing-function` | `ease-in-out` | Animation easing |

### CSS Parts

You can style each internal layout containers using the `::part()` selector:

| Part Name | Description |
| --- | --- |
| `header` | Header container |
| `sidebar` | Sidebar container |
| `content` | Content container |
| `sidebar_border` | Sidebar border/resize handle |

**Example:**

```css
page-skeleton::part(header) {
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
```

## Migration from @shaman-apprentice/ngx-page-skeleton

If you're upgrading from [@shaman-apprentice/ngx-page-skeleton](https://github.com/shaman-apprentice/ngx-page-skeleton), see this [migration guide](docs/migrate-from-ngx-page-skeleton.md).