# Bone Flow #022 - Hover Border Effect 操作ガイド

## 1. 最初に

#022は任意のHTMLElementの外周へ共通Border Effectを付与します。
Runtimeは `BoneHoverBorderEffect 1.0.12` です。
通常利用Classは `bf-hover-border-effect` です。
このGuideのLive Samplesはページ側integrationが実Runtimeをattachして表示します。

## 2. Hover Border Effectとは

対象要素そのものを描き直さず、document.body上のoverlayを対象矩形へ追従させます。
Effectは `normal / neon / spin / blink / dash / corner / chase / double / wave` の9種です。
Triggerは `mouseover / active / both` の3種です。

## 3. HTML Functional Class `bf-hover-border-effect`

最小利用は次の通りです。
```html
<button class="bf-hover-border-effect">Hover me</button>
```
Classだけの利用はRuntime Defaultでmountします。
Property値の詳細設定はProperty EditorまたはPublic APIを使用します。存在しないdata-*属性を追加しません。

## 4. 最小HTML

```html
<button id="sample">Sample</button>
<script>
  BoneHoverBorderEffect.attach(document.getElementById("sample"), { effect: "normal" });
</script>
```

## 5. 設定付きHTML / Public API

```js
const target = document.querySelector("#sample");
BoneHoverBorderEffect.attach(target, {
  trigger: "mouseover",
  effect: "neon",
  color: "#35f4ff",
  direction: "clockwise",
  speed: "normal",
  lineWidth: 3,
  spinWidth: 2,
  spinGlow: 12
});
```

## 6. Live Samples

この章をGuideで開くと、#017 TextのLive Guide例と同じ考え方で実Runtime Sampleが挿入されます。
normal / neon / spin / blink / dash / corner / chase / double / wave と mouseover / active / both を実HTMLElementで確認できます。

## 7. Runtime Default全件

`trigger = mouseover`
`effect = normal`
`color = #35f4ff`
`direction = clockwise`
`speed = normal`
`lineWidth = 3`
`spinWidth = 2`
`spinGlow = 12`

## 8. 正式Property定義（Runtime Schema）

正本は `BoneHoverBorderEffect.getPropertySchema()` です。
Defaultsは `getPropertyDefaults()`、1件取得は `getPropertyDefinition(key)` を使用します。

## 9. Property詳細

以下はv1.0.12実Runtimeから抽出した定義です。

## 9.1 `trigger`

型: enum
Default: `mouseover`
許容値: `mouseover / active / both`
無効値は `BONE_022_TRIGGER_INVALID`。

## 9.2 `effect`

型: enum
Default: `normal`
許容値: `normal / neon / spin / blink / dash / corner / chase / double / wave`
無効値は `BONE_022_EFFECT_INVALID`。

## 9.3 `color`

型: color string
Default: `#35f4ff`
#022 Property Editorでは#040 ColorPicker CallerのRETURN値をRuntimeへ渡します。

## 9.4 `direction`

型: enum
Default: `clockwise`
許容値: `clockwise / counterclockwise`
無効値は `BONE_022_DIRECTION_INVALID`。

## 9.5 `speed`

型: enum
Default: `normal`
許容値: `slow / normal / fast`
無効値は `BONE_022_SPEED_INVALID`。

## 9.6 `lineWidth`

型: number
Default: `3`
Runtime normalize範囲: `1..12 px`
対象: `normal / neon / blink / dash / corner / chase / double / wave` の外周線幅。
`spin` は専用の `spinWidth` を使用します。
範囲外数値はRuntimeで範囲内へnormalizeされます。

## 9.7 `spinWidth`

型: number
Default: `2`
Runtime normalize範囲: `1..24 px`
範囲外数値はRuntimeで範囲内へnormalizeされます。

## 9.8 `spinGlow`

型: number
Default: `12`
Runtime normalize範囲: `0..40 px`
範囲外数値はRuntimeで範囲内へnormalizeされます。

## 10. Effect 全9種

`normal`: 固定外周線。
`neon`: 外周を移動する発光線。
`spin`: 固定frameの外周mask内でconic rotorを回転。
`blink`: 外周線を点滅。
`dash`: 外周4辺の破線が周回。
`corner`: 四隅のL字外周だけを伸縮。
`chase`: 短い発光線1本が外周を周回。
`double`: 二重の外周線。
`wave`: 外周上の明暗帯が移動。

`cross` は削除済みで、Runtimeへ渡すと `BONE_022_EFFECT_INVALID` です。全9種ともHTMLElement内部を塗りつぶしません。

### Effect登録変更（正式）

- 削除: `cross`
- 追加: `dash / corner / chase / double / wave`
- 現行正式enum: `normal / neon / spin / blink / dash / corner / chase / double / wave`
- Runtime Schemaの `effect.enum`、Property UI、Guide Live Samples、Smoke、Validationのすべてをこの9種へ同期します。

## 11. Trigger 全3種

`mouseover`: mouseenterで表示、mouseleaveで非表示。
`active`: `setActive(target, true)` の間表示。
`both`: hoverまたはactiveのどちらかがtrueなら表示。

## 12. Public API

`defaults()`
`getPropertySchema()`
`getPropertyDefaults()`
`getPropertyDefinition(key)`
`normalizeOptions(options)`
`attach(target, options)`
`update(target, patch)`
`setActive(target, value)`
`refresh(target)`
`getOptions(target)`
`getState(target)`
`getOverlay(target)`
`isAttached(target)`
`detach(target)`

## 13. attach / update / detach

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "spin" });
BoneHoverBorderEffect.setActive(target, true);
BoneHoverBorderEffect.update(target, { speed: "fast" });
console.log(BoneHoverBorderEffect.getState(target));
BoneHoverBorderEffect.detach(target);
```

## 14. #040 ColorPicker

#022は独自ColorPickerを作りません。
#040の正式Caller APIを使用し、RETURNされたSolid色を `color` Propertyへ反映します。

## 15. #002 Bone Joint

Connection Imageは#002 BoneJoint 1.1.36の `evaluate()` / `drawLine()` / `drawError()` を使用します。
#022独自Joint線は作りません。
正常IN/OUT、Target不存在、TYPE mismatchを独立ケースとして表示します。

## 16. Smoke / Validation

Smoke TestはRuntimeのattach/update/setActive/detach、9 Effect、3 Trigger、button/input/panel、Runtime Schema、Functional Classを実DOMで検査します。
Validationは重複ID、無効enum拒否、Connection Image、Guide、#040 Caller、Completion gateを確認します。
Smoke/Validationは画面のボタンから実行し、結果DOMへ理由付きで表示します。

## 17. Completion

SmokeとValidationが両方PASSする前は完了ボタンを有効にしません。
ユーザーlocalhost確認前にCompleted / Specification Frozenとは扱いません。

## 18. Error / Troubleshooting

`BONE_022_TARGET_INVALID`: HTMLElement以外。
`BONE_022_NOT_ATTACHED`: attach前にupdate/setActive/refresh。
`BONE_022_TRIGGER_INVALID`: triggerが正式3値以外。
`BONE_022_EFFECT_INVALID`: effectが正式9値以外。`cross` も無効値です。
`BONE_022_DIRECTION_INVALID`: directionが正式2値以外。
`BONE_022_SPEED_INVALID`: speedが正式3値以外。

## 19. そのまま使えるサンプル

```html
<button id="hover-target">Hover Border</button>
```
```js
const el = document.getElementById("hover-target");
BoneHoverBorderEffect.attach(el, { trigger:"both", effect:"neon", color:"#35f4ff" });
// clickなどの状態変更時
BoneHoverBorderEffect.setActive(el, true);
```

## 20. 確認チェックリスト

- Runtime 1.0.12を読込
- Property → Previewが実連動
- 9 Effect確認
- 3 Trigger確認
- #040 Caller確認
- #002 Connection Image確認
- Smoke実行
- Validation実行
- Guide Live Samples確認
- HTML Functional Class確認
- Console Error 0を確認

## Appendix A. Property早見表

| Key | Type | Default | Values / Range |
|---|---|---|---|
| trigger | enum | mouseover | mouseover / active / both |
| effect | enum | normal | normal / neon / spin / blink / dash / corner / chase / double / wave |
| color | color string | #35f4ff | #040 Caller RETURN |
| direction | enum | clockwise | clockwise / counterclockwise |
| speed | enum | normal | slow / normal / fast |
| lineWidth | number | 3 | 1..12 px（spin以外の外周線幅） |
| spinWidth | number | 2 | 1..24 px |
| spinGlow | number | 12 | 0..40 px |

## Appendix B. Runtime state

`getState(target)` は `attached / hovered / active / visible / trigger / effect` を返します。

## Appendix C. 責務境界

#022はBorder Effectのみを所有します。ColorPickerは#040、接続判定・線・異常表示は#002、Guideの共有Modal/LoaderはBoneDocGuide/BoneDocLoaderが所有します。

## Appendix D. Effect × Trigger 実行Reference

### normal / mouseover

- effect: `normal`
- trigger: `mouseover`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "mouseover", effect: "normal" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### normal / active

- effect: `normal`
- trigger: `active`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "normal" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### normal / both

- effect: `normal`
- trigger: `both`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "both", effect: "normal" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### neon / mouseover

- effect: `neon`
- trigger: `mouseover`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "mouseover", effect: "neon" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### neon / active

- effect: `neon`
- trigger: `active`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "neon" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### neon / both

- effect: `neon`
- trigger: `both`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "both", effect: "neon" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### spin / mouseover

- effect: `spin`
- trigger: `mouseover`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "mouseover", effect: "spin" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### spin / active

- effect: `spin`
- trigger: `active`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "spin" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### spin / both

- effect: `spin`
- trigger: `both`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "both", effect: "spin" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### blink / mouseover

- effect: `blink`
- trigger: `mouseover`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "mouseover", effect: "blink" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### blink / active

- effect: `blink`
- trigger: `active`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "blink" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### blink / both

- effect: `blink`
- trigger: `both`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "both", effect: "blink" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### dash / mouseover

- effect: `dash`
- trigger: `mouseover`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "mouseover", effect: "dash" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### dash / active

- effect: `dash`
- trigger: `active`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "dash" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### dash / both

- effect: `dash`
- trigger: `both`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "both", effect: "dash" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### corner / mouseover

- effect: `corner`
- trigger: `mouseover`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "mouseover", effect: "corner" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### corner / active

- effect: `corner`
- trigger: `active`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "corner" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### corner / both

- effect: `corner`
- trigger: `both`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "both", effect: "corner" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### chase / mouseover

- effect: `chase`
- trigger: `mouseover`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "mouseover", effect: "chase" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### chase / active

- effect: `chase`
- trigger: `active`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "chase" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### chase / both

- effect: `chase`
- trigger: `both`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "both", effect: "chase" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### double / mouseover

- effect: `double`
- trigger: `mouseover`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "mouseover", effect: "double" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### double / active

- effect: `double`
- trigger: `active`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "double" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### double / both

- effect: `double`
- trigger: `both`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "both", effect: "double" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### wave / mouseover

- effect: `wave`
- trigger: `mouseover`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "mouseover", effect: "wave" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### wave / active

- effect: `wave`
- trigger: `active`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "active", effect: "wave" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

### wave / both

- effect: `wave`
- trigger: `both`
- attach後は `isAttached(target) === true` を確認します。
- `getState(target).visible` はTrigger状態から決まります。
- `detach(target)` 後は `isAttached(target) === false` を確認します。

```js
BoneHoverBorderEffect.attach(target, { trigger: "both", effect: "wave" });
const state = BoneHoverBorderEffect.getState(target);
BoneHoverBorderEffect.detach(target);
```

## Appendix E. HTMLElement別Reference

### button

対象: `button`
- attach対象はnodeType 1かつgetBoundingClientRectを持つHTMLElementです。
- geometryは対象の実getBoundingClientRectからoverlayへ反映されます。
- borderRadiusはgetComputedStyle(target).borderRadiusを利用します。

### input

対象: `input`
- attach対象はnodeType 1かつgetBoundingClientRectを持つHTMLElementです。
- geometryは対象の実getBoundingClientRectからoverlayへ反映されます。
- borderRadiusはgetComputedStyle(target).borderRadiusを利用します。

### div(panel)

対象: `div(panel)`
- attach対象はnodeType 1かつgetBoundingClientRectを持つHTMLElementです。
- geometryは対象の実getBoundingClientRectからoverlayへ反映されます。
- borderRadiusはgetComputedStyle(target).borderRadiusを利用します。

## Appendix F. Browser QA操作手順

### F.1 Property → Preview

1. Triggerを変更する。
2. Effectを変更する。
3. Colorを#040 CallerからRETURNする。
4. Direction / Speed / Spin Width / Spin Glowを変更する。
5. `PropertyをRuntimeへ反映` を押す。
6. `getOptions(target)` と画面表示が一致することを確認する。
7. `getState(target)` とAttached / Visible表示が一致することを確認する。

### F.2 active

1. Trigger=`active` を選ぶ。
2. `active=true` をONにする。
3. Effectが表示される。
4. OFFにすると非表示になる。

### F.3 mouseover

1. Trigger=`mouseover` を選ぶ。
2. Previewへmouse enterする。
3. Effectが表示される。
4. mouse leaveで非表示になる。

### F.4 both

1. Trigger=`both` を選ぶ。
2. hoverまたはactiveのどちらか一方がtrueなら表示される。
3. 両方falseなら非表示になる。

### F.5 detach / reattach

1. detachを押す。
2. `isAttached(target) === false` を確認する。
3. overlayがDOMから除去されることを確認する。
4. reattachを押す。
5. `isAttached(target) === true` を確認する。

### F.6 Smoke Test

Smoke Test実行後、各行にPASS/FAILと理由が表示されます。単なる関数存在確認だけではなく、実DOMへattach/update/setActive/detachを行います。

### F.7 Validation

Validation実行前にSmokeがPASSしている必要があります。無効enum、重複ID、Runtime Schema defensive copy、Connection Image、Guide入口、#040 Caller、Completion gateを確認します。

### F.8 全テスト / クリア

`全テスト実行` はSmoke→Validationの順で実行します。`結果クリア` はSmoke / Validation / OverallをNOT RUNへ戻します。

### F.9 Guide Live Samples

操作ガイドを開き、normal / neon / spin / blink / dash / corner / chase / double / wave の9カードと、mouseover / active / both の3カードが表示されることを確認します。これらは静的画像ではなく実Runtimeです。

### F.10 Console

Runtime ConsoleとブラウザConsoleを確認し、JavaScript Errorが残っていないことを確認します。


## v1.0.27 表示・レポート整合
Runtime は BoneHoverBorderEffect 1.0.12。テスト画面、Completion表示、保存レポート、Runtime identity検査はすべて1.0.12へ統一する。#022自身のRuntime/CSSはversion query付きURLで読み込み、旧1.0.11のブラウザキャッシュを再利用しない。
