> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/web/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/web/css/layout/system/box.md).

# Box

基本上不是作為排版的元件，但具有背景、邊框等。

{% tabs %}
{% tab title="👥 相關" %}

* Layout ⟩ [Dark Theme](/web/css/layout/system/dark-theme.md)
  {% endtab %}

{% tab title="Second Tab" %}

{% endtab %}
{% endtabs %}

{% hint style="info" %}
設計原則：

* 具有「**有形的邊界**」：設計時需搭配 border, background-color, color
* 與內文「**有一致的間距**」：padding
  {% endhint %}

{% hint style="warning" %}
box.mjs **更改樣式的策略**與 [vstack.mjs](/web/css/layout/system/vstack.md#version) **不同**。

* box.mjs 利用 setters 將樣式直接寫到個別的 \<box> 樣式 (**this.style**) 中，因此無法共用。
* vstack.mjs 則是間接將**客製化的 \<style> 樣式**寫到 **\<head>** 中，並同時將此 \<style> 的**專有 ID** 寫到 \<vstack> 的 "**data-style**" 屬性中，希望能**共用**這個 \<style>。
  {% endhint %}

## source code

{% tabs %}
{% tab title="live" %}
{% embed url="<https://codesandbox.io/s/layout-v-0-0-5-box-2et3m?file=/layout/box.mjs>" %}

{% endtab %}

{% tab title="box.mjs" %}

```javascript
// 0.0.9 - Box: remove render(), use setters instead.

import {attr} from './helpers.mjs';

/**
 * Box
 * ---
 * <box- padding="" border="" background="" color="" dark>
 */
export default class Box extends HTMLElement {

    // ⭐ observed attributes
    static get observedAttributes() {
        return [
            'padding', 'border', 'color', 'background', 'dark'
        ];
    }

    // re-renders whenever one of attributes changes.
    attributeChangedCallback(attr, oldValue, newValue) {
        // ⭐️ 更新：全部透過 setter 處理就好。
        this[attr] = this[attr];
    }

    // -------------- ⭐️ getters/setters ------------------

    // this.padding
    get padding() { return attr(this, 'padding') }
    set padding(val) { 
        this.style.padding = val;
        return attr(this, 'padding', val) 
    }

    // this.border
    get border() { return attr(this, 'border') }
    set border(val) { 
        this.style.border = val;
        return attr(this, 'border', val) 
    }

    // this.color
    get color() { return attr(this, 'color') }
    set color(val) { 
        this.style.color = val;
        return attr(this, 'color', val) 
    }

    // this.background
    get background() { return attr(this, 'background') }
    set background(val) { 
        this.style.background = val;
        return attr(this, 'background', val) 
    }

    // this.dark
    get dark() { return this.hasAttribute('dark') }
    set dark(val) { 
        this.classList.toggle('dark', val);
        return attr(this, 'dark', val);
    }
    
}

// ⭐️ register <box->
customElements.get('box-') || 
customElements.define('box-', Box);
```

{% endtab %}

{% tab title="box.css" %}

```css
/* ⭐️ Box 0.0.6 + border */

/* box */
box- {

    /* default settings */
    --box-light-color: #eee;
    --box-dark-color: #222;

    display: block;
    
    /* 🔸 axiom: box's content should be away with its edges */
    padding: var(--spacing);

    /* 🔸 axiom: box should be visible */
    border: var(--border-thin);
    background-color: var(--box-light-color);
    color: var(--box-dark-color);

    /* 
        High contrast themes tend to eliminate backgrounds. By employing a transparent outline the box shape can be restored.

        When a high contrast theme is not running, the outline is invisible.
     */
    outline: 0.125rem solid transparent; 
    outline-offset: -0.125rem;
}

/* 
    box (dark theme) 
    ----------------
    In a greyscale design, it is possible to switch between 
    dark/light theme with `filter`.

    box.invert { filter: invert(100%); }
*/
box-.dark {
    color: var(--box-light-color);
    background-color: var(--box-dark-color);
    border: var(--border-thin);
}

/* elements in box */
box- * {
    /* 
        Changing `background-color` requires you to change `color` to ensure the content is still legible. This can be made easier by 
        applying `color: inherit` to any elements inside box.
     */
    color: inherit;
}
```

{% endtab %}

{% tab title="👥 相關" %}

* [VStack](/web/css/layout/system/vstack.md), [HStack](/web/css/layout/system/hstack.md)
* [Observed Attributes](/web/component/custom-element/observed-attributes.md)
  {% endtab %}

{% tab title="⬆️ 需要" %}

* [helper functions](/web/appendix/custom/helper-functions.md)
  {% endtab %}

{% tab title="💾 程式" %}

* codepen ⟩ [measure](https://codepen.io/lochiwei/pen/qBjeQdE)
* codesandbox ⟩ [box (0.0.5)](https://codesandbox.io/s/layout-v-0-0-5-box-2et3m?file=/style.css)
* replit ⟩&#x20;
  * [this\[prop\] = this\[prop\] ?](https://replit.com/@pegasusroe/thisprop-thisprop#index.js) - 讀取屬性並強迫再設定一次屬性❗️
  * [refactor Box](https://replit.com/@pegasusroe/refactor-lessboxgreater#box.mjs) - remove render(), use **attributeChangedCallback**() instead
  * [layout (0.0.9)](https://replit.com/@pegasusroe/layout-009-refactor-Box#layout/box.mjs)
    {% endtab %}
    {% endtabs %}
