> ## Oymyakon DS index
> Fetch the full index at: https://super-dollop-pzmo65r.pages.github.io/llms.txt
> HTML preview: https://super-dollop-pzmo65r.pages.github.io/decorate-container.html
> Source: specs/primitives/decorate-container.md

---

# DecorateContainer · Oymyakon DS 3

> A wrapper that decorates any component: the hosted component sits in Slot#1, and up to two
> decorator slots place a badge, status indicator, icon, or Lottie above it — to highlight new
> features, alerts, notification counts, and special actions.

**Version:** 3.3.0 · **Status:** Draft
**Figma:** `DecorateContainer 3.3` — Specification frame `26994:5398` in the main components file
(the component set's node-ids are uncaptured: the metadata endpoint fails on this frame — capture
them on the next Figma pass)

---

## 1. Description

DecorateContainer wraps a component — a button, an avatar, a tile — and lets a decoration float
above it without touching the hosted component's own contract. The container never affects the
states of what it hosts; it only owns the wrapping geometry, the decorator anchoring, and the
decorator's enter/exit animation.

| Slot | Role |
|---|---|
| **Slot#1** | The hosted component — the container for placing components |
| **Slot#2** | The decorator displayed above the Slot#1 content |
| **Slot#3** | An additional decorator above Slot#1 — used independently or together with Slot#2 |

---

## 2. Anatomy

```
DecorateContainer              .decorate-container — hug wrapper, position:relative
├── Slot#1                     .decorate-container__slot1 — the hosted component
├── Slot#2                     .decorate-container__slot2 — decorator, absolute, anchored
└── [Slot#3]                   .decorate-container__slot3 — second decorator, same contract
```

| Element | Required | Description |
|---|---|---|
| **Slot#1** | Required | Wraps the component on all sides: width/height hug, `s0` default paddings, `s0` default corner radius. Horizontal and vertical margins are instance-set and may be negative |
| **Slot#2** | Optional | The decorator: hug, `s0` paddings, `s0` radius. Anchored per § 3; expands towards the direction opposite to its offset, so it never pushes into adjacent screen content |
| **Slot#3** | Optional | Same contract as Slot#2; independent of it |

---

## 3. Properties

| Property | Values | Default |
|---|---|---|
| `horizontal` (per decorator) | `right` / `center` / `left` | `right` |
| `vertical` (per decorator) | `top` / `center` / `bottom` | `top` |
| `offset` (per decorator) | instance-set, may be negative | `0` |
| `animation` | on / off | on (§ 6) |

**Predefined Slot#2 / Slot#3 content** (the slots also accept any DS or local component — Custom):

| Content | Default form |
|---|---|
| [Indicator](https://super-dollop-pzmo65r.pages.github.io/indicator.md) | Simple style, M size |
| [IconContainer](https://github.com/inDriver/oymyakon-ds/blob/main/specs/primitives/icon-container.md) | `s24` |
| [ImageContainer](https://github.com/inDriver/oymyakon-ds/blob/main/specs/primitives/image-container.md) | `s24` (a size-class the container's recipe table gains when first used) |
| [Tag](https://super-dollop-pzmo65r.pages.github.io/tag.md) | Surface style |
| Custom | any component from the design system or a local one |

---

## 4. States

DecorateContainer has no states of its own and does not affect the states of the components placed
inside it. Standard is its only form; a hosted component's pressed/disabled/skeleton life stays
entirely the hosted component's business.

---

## 5. RTL

Slot#2 / Slot#3 mirror their **horizontal** placement from the document's dir: `right` becomes
`left`, `left` becomes `right`, `center` remains. Vertical placement never changes. On web the
anchoring uses logical inline properties, so the mirror needs no extra class.

---

## 6. Animation

The decorator appears and disappears with the **Scale Short** pattern (`motion-rules.md` § 5.1);
animation can be disabled per instance.

| Event | Token | Value |
|---|---|---|
| Decorator enter | `var(--pattern-scale-short-appear)` | 100 ms, standard-ease-in-out |
| Decorator exit | `var(--pattern-scale-short-hide)` | 200 ms, standard-ease-in-out |

Only `transform: scale(0 ↔ 1)` animates, from the decorator's own center.
Reduced motion is zeroed at the token; the primitive ships no override of its own.

---

## 7. CSS implementation

```css
.decorate-container { position: relative; display: inline-flex; }
.decorate-container__slot1 { display: inline-flex; }
.decorate-container__slot2,
.decorate-container__slot3 {
  position: absolute;
  display: inline-flex;
  transition: transform var(--pattern-scale-short-appear);
}
/* Anchors — logical, so RTL mirrors on its own. Default: top right. */
.decorate-container__slot2 { inset-inline-end: var(--dc-offset-x, 0); top: var(--dc-offset-y, 0); }
/* h/v modifiers move the anchor; --off plays the exit */
.decorate-container__slot2--off { transform: scale(0); transition: transform var(--pattern-scale-short-hide); }
```

The full anchor matrix (`--h-left/--h-center`, `--v-center/--v-bottom` per decorator slot) lives in
`src/shared/shared.css`. The instance offsets are the two custom properties
`--dc-offset-x` / `--dc-offset-y` (SP tokens; negative values allowed by the contract).

---

## 8. Usage context

**Use it** whenever a decoration must float above a component: a NEW tag on a service tile, an
unread-count Indicator on an icon, a status dot on an avatar (Slot#3 example: a check badge at the
avatar's bottom corner).

**Do not use it for:**

- Content that is part of the hosted component's own anatomy — a Cell's end-slot chevron is the
  Cell's business, not a decoration.
- Blocking or persistent messaging — that is the alert family, not a badge.
- A decorator that must push layout around it — the decorator floats and never affects flow.

Related: [Indicator](https://super-dollop-pzmo65r.pages.github.io/indicator.md), [Tag](https://super-dollop-pzmo65r.pages.github.io/tag.md),
[IconContainer](https://github.com/inDriver/oymyakon-ds/blob/main/specs/primitives/icon-container.md), [ImageContainer](https://github.com/inDriver/oymyakon-ds/blob/main/specs/primitives/image-container.md); consumed by ServiceCard's
DecorateContainer slot (`specs/components/service-card/service-card.md` § 3).

---

## 9. Rules

1. The container never alters the hosted component — no state, colour, or size bleed.
2. Decorators float: they never affect the flow around the container.
3. A decorator expands towards the direction opposite to its offset.
4. Size-increase parameters (100/130/150 %) are set within the hosted components themselves;
   DecorateContainer does not restrict or affect them.

---

## 10. Accessibility

DecorateContainer announces elements according to the hosted component's behaviour. Slot#1, Slot#2
and Slot#3 are perceived as a **single focus area** — the decorator never becomes a separate swipe
stop; its meaning joins the hosted component's announcement. The focus rectangle covers the whole
construction, decorator overhangs included.

**iOS · VoiceOver.** The container is the accessibility element
(`isAccessibilityElement = true` on the wrapper; the hosted component and both decorators are
`false`). The trait comes from the hosted component (a Button host keeps the Button trait); the
label is the host's label with the decorators' meaning appended — "Menu, 1 notification";
"Profile photo, verified, online". A decorator with no meaning of its own (a purely visual dot)
adds nothing to the label.

**Android · TalkBack.** The container is the single focusable node
(`screenReaderFocusable`, `focusable`); the slots inside are `importantForAccessibility="no"`.
The role mirrors the hosted component's; a count or status decorator lands in
`stateDescription`, so TalkBack reads "Menu, button, 1 notification" as one stop and re-announces
when the count changes.

**Web.** The decorator slots carry `aria-hidden="true"`; the hosted element keeps its own focus
behaviour, and a meaningful decorator's text joins the host's accessible name. The Scale Short
enter/exit never announces on its own — a decorator whose appearance must be heard is the hosted
component's live-region decision, not the container's.

---

## 11. Analytics and coverage contract

Not a coverage unit. The container is structural: no `data-ds-component`, no targets, no actions.
The hosted component keeps its own contract untouched; a tappable decorator (if a product ever
wires one) is that component's own target, not the container's. See
`docs/prototype-analytics-and-coverage.md` for the structural-node policy.

---

## Changelog

| Version | Date | Change |
|---|---|---|
| 3.3.0 | 2026-09-11 | First repo spec, from the Figma `DecorateContainer 3.3` Specification frame (`26994:5398`): three-slot anatomy (content + two independent decorators), 3×3 anchoring with right/top defaults, instance offsets (negative allowed) with opposite-direction expansion, predefined content (Indicator Simple M, IconContainer s24, ImageContainer s24, Tag Surface, Custom), Scale Short enter/exit (100/200 ms) with per-instance disable, RTL horizontal mirror, single-focus-area screen-reader contract. Web build ships in `shared.css`. |
