CSS 레이아웃 API 레벨 1

W3C 최초 공개 작업 초안,

이 버전:
https://www.w3.org/TR/2018/WD-css-layout-api-1-20180412/
최신 공개 버전:
https://www.w3.org/TR/css-layout-api-1/
편집자 초안:
https://drafts.css-houdini.org/css-layout-api-1/
피드백:
public-houdini@w3.org 제목 줄에 “[css-layout-api] … 메시지 주제 …” 포함 (아카이브)
이슈 추적:
GitHub
명세 내 인라인
편집자:
(Microsoft)
(Google)
Tab Atkins-Bittner (Google)
(Google)
(Microsoft)

요약

이 명세서는 개발자가 계산된 스타일과 박스 트리 변경에 따라 박스의 레이아웃을 할 수 있도록 하는 API를 설명합니다.

이 문서의 상태

이 섹션은 출판 시점에서의 문서 상태를 설명합니다. 다른 문서가 이 문서를 대체할 수 있습니다. 현재 W3C 출판물 목록과 이 기술 보고서의 최신 버전은 W3C 기술 보고서 인덱스 https://www.w3.org/TR/에서 확인할 수 있습니다.

이 문서는 최초 공개 작업 초안입니다.

최초 공개 작업 초안으로 발행된다고 해서 W3C 회원의 승인이나 지지를 의미하지 않습니다. 이 문서는 초안이며 언제든지 업데이트, 교체 또는 폐기될 수 있습니다. 진행 중인 작업 외의 용도로 인용하는 것은 적절하지 않습니다.

GitHub 이슈에서 이 명세에 대한 논의를 권장합니다. 이슈를 등록할 때 제목에 “css-layout-api”를 포함해 주시고, 가능하다면 다음과 같이 작성해 주세요: “[css-layout-api] …의견 요약…”. 모든 이슈와 코멘트는 아카이브에 보관됩니다.

이 문서는 CSS 작업 그룹에서 작성되었습니다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 작성되었습니다. W3C는 그룹 산출물과 관련하여 제출된 공개 특허 목록을 유지하며, 해당 페이지에는 특허 공개 방법에 대한 안내도 포함되어 있습니다. 특정 특허가 필수 청구를 포함한다고 믿는 개인은 W3C 특허 정책 6절에 따라 정보를 공개해야 합니다.

이 문서는 2018년 2월 1일 W3C 프로세스 문서의 적용을 받습니다.

1. 소개

이 섹션은 규범적이지 않습니다.

CSS의 레이아웃 단계는 프래그먼트박스 트리로부터 생성하고 위치를 지정하는 역할을 합니다.

이 명세서는 개발자가 계산된 스타일 및 박스 트리 변경에 따라 박스의 레이아웃을 할 수 있도록 하는 API를 설명합니다.

2. 레이아웃 API 컨테이너

대체 값이 <display-inside> 생성식에 추가됩니다: layout(<ident>).

layout()
이 값은 요소가 레이아웃 API 컨테이너 박스를 생성하도록 합니다.

레이아웃 API 컨테이너<display-inside> 계산된 값layout()인 요소가 생성하는 박스입니다.

레이아웃 API 컨테이너는 자신의 내용에 대해 새로운 레이아웃 API 서식 컨텍스트를 설정합니다. 이는 블록 서식 컨텍스트를 설정하는 것과 같지만, 블록 레이아웃 대신 저자가 제공한 레이아웃이 사용됩니다. 예를 들어, 플로트는 레이아웃 API 컨테이너에 침범하지 않으며, 컨테이너의 마진은 내용물의 마진과 병합되지 않습니다.

레이아웃 API 컨테이너의 모든 인플로우 자식은 레이아웃 API 자식이라 하며, 저자가 정의한 레이아웃을 사용하여 배치됩니다.

레이아웃 API 컨테이너는 자신의 내용에 대해 블록 컨테이너와 동일하게 포함 블록을 형성합니다. [CSS21]

참고: 향후 명세 레벨에서는 포함 블록 동작을 오버라이드할 수 있는 방법이 추가될 수 있습니다.

overflow 속성은 레이아웃 API 컨테이너에 적용됩니다. 이는 §4.3 Overflow에서 다룹니다.

레이아웃이 전체적으로 저자에게 달려 있으므로, 다른 레이아웃 모드(예: flex, block)에서 사용되는 속성들이 반드시 적용되진 않을 수 있습니다. 예를 들어 저자가 자식의 margin 속성을 존중하지 않을 수도 있습니다.

2.1. 레이아웃 API 컨테이너 페인팅

레이아웃 API 컨테이너의 자식들은 인라인 블록과 정확히 동일하게 페인트됩니다 [CSS21], 단 레이아웃 메서드에서 반환되는 순서(childFragments)가 문서의 원래 순서 대신 사용되고, z-index 값이 auto가 아닌 경우에는 positionstatic이어도 스태킹 컨텍스트가 생성됩니다.

2.2. 박스 트리 변환

레이아웃 API 자식layout options' childDisplay 값(클래스의 layoutOptions로 설정)에 따라 다르게 동작할 수 있습니다.

layout options' childDisplay 값이 "block"이면 해당 자식의 display 값이 blockified됩니다. 이는 flex 컨테이너grid 컨테이너의 자식들과 유사합니다. [css3-display]를 참고하세요.

layout options' childDisplay 값이 "normal"이면 blockification이 발생하지 않습니다. 대신 <display-outside> 계산된 값inline인 자식(root inline box)은 LayoutFragment를 각 줄마다 하나씩 생성하며, layoutNextFragment()가 호출될 때마다 반환합니다.

참고: 저자는 각 줄의 사용 가능한 인라인 크기를 조정하고, 각 줄을 개별적으로 위치시킬 수 있습니다.

LayoutChildroot inline box를 나타내는 경우에도 추가적인 변환이 적용됩니다.

위 두 경우 모두 자식들은 원자 인라인(atomic inlines)이 됩니다.

참고: 사용자 에이전트는 블록 레벨 박스를 만났을 때 어떤 "인라인 분할"이나 프래그먼트화를 수행하지 않습니다.

참고: 아래 예시에서 "inline-span"은 하나의 LayoutChild로 표현되며, "block"과 "float" 모두 원자 인라인이 됩니다.
<span id="inline-span">
  Text
  <div id="block"></div>
  <div id="float"></div>
  Text
</span>

3. 레이아웃 API 모델 및 용어

이 섹션은 저자에게 제공되는 레이아웃 API 개요를 설명합니다.

현재 레이아웃은 지금 레이아웃을 수행 중인 박스에 대한 레이아웃 알고리즘입니다.

부모 레이아웃박스의 직접적인 부모에 대한 레이아웃 알고리즘(현재 레이아웃을 요청하는 알고리즘)입니다.

자식 레이아웃LayoutChild현재 레이아웃에 대한 레이아웃 알고리즘입니다.

3.1. 레이아웃 자식

[Exposed=LayoutWorklet]
interface LayoutChild {
    readonly attribute StylePropertyMapReadOnly styleMap;

    IntrinsicSizesRequest intrinsicSizes();
    LayoutFragmentRequest layoutNextFragment(LayoutConstraints constraints, ChildBreakToken breakToken);
};

LayoutChild는 내부 슬롯을 가집니다:


LayoutChild는 레이아웃이 발생하기 전 CSS 박스를 나타냅니다. (박스들은 모두 display의 계산된 값이 none이 아닙니다.)

LayoutChild는 자체적으로 레이아웃 정보(인라인 또는 블록 크기 등)를 포함하지 않지만, 레이아웃 정보를 포함하는 LayoutFragment를 생성할 수 있습니다.

저자는 이 API로 LayoutChild를 직접 생성할 수 없으며, 이는 렌더링 엔진의 별도 단계(스타일 해석 이후)에서 생성됩니다.

LayoutChild는 다음에 의해 생성될 수 있습니다:

참고: 아래 예시의 경우 세 개의 LayoutChild로 배치됩니다:
<style>
  #box::before { content: 'hello!'; }
</style>
<div id="box">A block level box with text.</div>
<img src="..." />
참고: 아래 예시는 하나의 LayoutChild로 배치되며, root inline box를 공유합니다:
This is a next node, <span>with some additional styling,
that may</span> break over<br>multiple lines.

여러 개의 원자 인라인이 아닌 경우, 동일한 LayoutChild 내에 배치되어 렌더링 엔진이 요소 경계를 넘어 텍스트 셰이핑을 수행할 수 있도록 합니다.

참고: 아래 예시는 하나의 LayoutFragment를 생성해야 하지만, 세 개의 원자 인라인이 아닌 경우입니다:
ع<span style="color: blue">ع</span>ع

현재 박스를 레이아웃하는 레이아웃 메서드에 LayoutChild 배열이 자식으로 전달됩니다.

styleMapLayoutChild this에서 가져올 때, 사용자 에이전트는 다음 단계를 수행해야 합니다:
  1. thisStylePropertyMapReadOnly[[styleMap]] 내부 슬롯에 담아 반환합니다.

layoutNextFragment(constraints, breakToken) 메서드를 LayoutChild this에서 호출할 때, 사용자 에이전트는 다음 단계를 수행해야 합니다:
  1. request를 새 LayoutFragmentRequest로 생성하고 내부 슬롯을 설정합니다:

  2. request를 반환합니다.

intrinsicSizes() 메서드를 LayoutChild this에서 호출할 때, 사용자 에이전트는 다음 단계를 수행해야 합니다:
  1. request를 새 IntrinsicSizesRequest로 생성하고 내부 슬롯을 설정합니다:

  2. request를 반환합니다.

참고: layoutNextFragment()intrinsicSizes()는 동기적으로 실행되지 않습니다. 전체 설명은 §5.5.1 Request Objects를 참고하세요.

3.1.1. LayoutChildren와 박스 트리

box[[layoutChildMap]] 내부 슬롯을 가지며, 이는 map으로 LayoutWorkletGlobalScopeLayoutChildren을 연결합니다.

사용자 에이전트가 레이아웃 자식 가져오기workletGlobalScope, name, box에 대해 수행하려고 할 때, 다음 단계를 실행해야 합니다:
  1. 다음을 단언합니다:

  2. layoutChildMapbox[[layoutChildMap]]으로 설정합니다.

  3. 만약 layoutChildMap[workletGlobalScope]이 존재하지 않는다면, 다음 단계를 실행합니다:

    1. definition레이아웃 정의 가져오기의 결과로 name, workletGlobalScope를 인자로 얻습니다.

      레이아웃 정의 가져오기가 성공했으며, definition"invalid"가 아님을 단언합니다.

    2. childInputPropertiesdefinitionchild input properties로 설정합니다.

    3. layoutChild를 새 LayoutChild로 생성하며, 내부 슬롯:

    4. 설정 layoutChildMap[workletGlobalScope] = layoutChild

  4. 가져오기 layoutChildMap[workletGlobalScope]의 결과를 반환합니다.

box박스 트리에 삽입될 때, 사용자 에이전트는 모든 LayoutWorkletGlobalScope에 대해 [[layoutChildMap]]을 미리 채울 수 있습니다.

box박스 트리에서 제거될 때, 사용자 에이전트는 [[layoutChildMap]]을 반드시 초기화합니다.

사용자 에이전트가 레이아웃 자식 스타일 업데이트box에 대해 수행하려고 할 때, 다음 단계를 실행해야 합니다:
  1. 다음을 단언합니다:

  2. 만약 box포함 블록레이아웃 API 컨테이너가 아니라면, 모든 단계를 중단합니다.

  3. layoutChildMapbox[[layoutChildMap]]으로 설정합니다.

  4. layoutChild에 대해 layoutChildMap에서:

    1. styleMaplayoutChild[[styleMap]]으로 설정합니다.

    2. box의 새 계산된 스타일에 따라 styleMapdeclarations를 업데이트합니다.

box의 계산된 스타일이 변경되면 사용자 에이전트는 레이아웃 자식 스타일 업데이트 알고리즘을 실행해야 합니다.

3.2. 레이아웃 프래그먼트

[Exposed=LayoutWorklet]
interface LayoutFragment {
    readonly attribute double inlineSize;
    readonly attribute double blockSize;

    attribute double inlineOffset;
    attribute double blockOffset;

    readonly attribute any data;

    readonly attribute ChildBreakToken? breakToken;
};

LayoutFragment는 내부 슬롯을 가집니다:


LayoutFragmentLayoutChild에 대한 레이아웃이 완료된 후의 CSS 프래그먼트를 나타냅니다. 이는 layoutNextFragment() 메서드에서 생성됩니다.

LayoutFragmentinlineSizeblockSize 속성을 가지며, 이는 각각 자식의 레이아웃 알고리즘에서 설정됩니다. 이 값들은 CSS 프래그먼트border box 크기를 나타내며, 현재 레이아웃의 작성 모드에 상대적입니다.

inlineSizeblockSize 속성은 변경할 수 없습니다. 현재 레이아웃에서 다른 inlineSize 또는 blockSize가 필요하다면, 저자는 다른 인자로 layoutNextFragment()를 다시 수행해야 합니다.

현재 레이아웃 내부의 저자는 생성된 LayoutFragmentinlineOffsetblockOffset 속성을 설정하여 위치를 지정할 수 있습니다. 설정하지 않으면 기본값은 0입니다. inlineOffsetblockOffsetLayoutFragment가 부모의 border box에 상대적인 위치를 나타내며, 변환이나 포지셔닝이 적용되기 전입니다(예: 프래그먼트가 상대 위치 지정된 경우).

레이아웃 알고리즘은 블록 방향으로 프래그먼트를 순차적으로 배치하며, 인라인 방향으로 자식들을 중앙에 위치시킵니다.
registerLayout('block-like', class {
    *intrinsicSizes(children, edges, styleMap) {
      const childrenSizes = yield children.map((child) => {
          return child.intrinsicSizes();
      });

      const maxContentSize = childrenSizes.reduce((max, childSizes) => {
          return Math.max(max, childSizes.maxContentSize);
      }, 0) + edges.all.inline;

      const minContentSize = childrenSizes.reduce((max, childSizes) => {
          return Math.max(max, childSizes.minContentSize);
      }, 0) + edges.all.inline;

      return {maxContentSize, minContentSize};
    }

    *layout(children, edges, constraints, styleMap) {
        const availableInlineSize = constraints.fixedInlineSize - edges.all.inline;
        const availableBlockSize = (constraints.fixedBlockSize || Infinity) - edges.all.block;

        const childFragments = [];
        const childConstraints = { availableInlineSize, availableBlockSize };

        const childFragments = yield children.map((child) => {
            return child.layoutNextFragment(childConstraints);
        });

        let blockOffset = edges.all.blockStart;
        for (let fragment of childFragments) {
            // 프래그먼트를 블록 방식으로 배치하고, 인라인 방향으로 중앙에 위치시킨다.
            fragment.blockOffset = blockOffset;
            fragment.inlineOffset = Math.max(
                edges.all.inlineStart,
                (availableInlineSize - fragment.inlineSize) / 2);

            blockOffset += fragment.blockSize;
        }

        const autoBlockSize = blockOffset + edges.all.blockEnd;

        return {
            autoBlockSize,
            childFragments,
        };
    }
});

레이아웃 API 컨테이너data 속성을 사용하여 다른 레이아웃 API 컨테이너들과 통신할 수 있습니다. 이 값은 data 멤버와 FragmentResultOptions 딕셔너리에서 설정됩니다.

LayoutFragmentbreakTokenLayoutChild가 마지막으로 프래그먼트된 위치를 지정합니다. breakToken이 null이면 LayoutChild는 해당 토큰 체인에 대해 더 이상 LayoutFragment를 생성하지 않습니다. breakTokenlayoutNextFragment() 함수에 전달되어 특정 자식의 다음 LayoutFragment를 생성합니다. breakToken은 변경할 수 없습니다. 현재 레이아웃에서 다른 breakToken이 필요하다면, 저자는 다른 인자를 사용해 layoutNextFragment()를 다시 실행해야 합니다.

사용자 에이전트가 레이아웃 프래그먼트 생성generator, layoutFragmentRequest, internalFragment에 대해 수행하려고 할 때, 다음 단계를 실행해야 합니다:
  1. targetRealmgeneratorRealm으로 설정합니다.

  2. fragment를 새 LayoutFragment로 생성하며:

  3. fragment를 반환합니다.

3.3. 내재적 크기

[Exposed=LayoutWorklet]
interface IntrinsicSizes {
  readonly attribute double minContentSize;
  readonly attribute double maxContentSize;
};

IntrinsicSizes 객체는 내부 슬롯을 가집니다:


IntrinsicSizes 객체는 CSS boxmin-content 크기max-content 크기를 나타냅니다. minContentSizemaxContentSize 속성은 현재 레이아웃LayoutChildborder box min/max-content 기여도를 나타냅니다. 속성 값은 현재 레이아웃의 인라인 방향에 상대적입니다.

minContentSizemaxContentSize는 변경할 수 없습니다. 이는 LayoutChild가 현재 레이아웃 패스 내에서 반드시 변하지 않아야 합니다.

아래 예시는 두 자식의 border-box 내재적 크기를 보여줍니다.
<style>
.child-0 {
  width: 380px;
  border: solid 10px;
}

.child-1 {
  border: solid 5px;
}

.box {
  display: layout(intrinsic-sizes-example);
  font: 25px/1 Ahem;
}
</style>

<div class="box">
  <div class="child-0"></div>
  <div class="child-1">XXX XXXX</div>
</div>
registerLayout('intrinsic-sizes-example', class {
    *intrinsicSizes(children, edges, styleMap) {
      const childrenSizes = yield children.map((child) => {
          return child.intrinsicSizes();
      });

      childrenSizes[0].minContentSize; // 400, (380+10+10) 고정 크기 자식
      childrenSizes[0].maxContentSize; // 400, (380+10+10) 고정 크기 자식

      childrenSizes[1].minContentSize; // 100, "XXXX"의 크기
      childrenSizes[1].maxContentSize; // 200, "XXX XXXX"의 크기
    }

    *layout() {}
});
사용자 에이전트가 내재적 크기 객체 생성intrinsicSizesRequest, internalIntrinsicSizes에 대해 수행하려고 할 때, 다음 단계를 실행해야 합니다:
  1. intrinsicSizes를 새 IntrinsicSizes로 생성하며:

  2. intrinsicSizes를 반환합니다.

3.4. 레이아웃 제약 조건

[Constructor(optional LayoutConstraintsOptions options),Exposed=LayoutWorklet]
interface LayoutConstraints {
    readonly attribute double availableInlineSize;
    readonly attribute double availableBlockSize;

    readonly attribute double? fixedInlineSize;
    readonly attribute double? fixedBlockSize;

    readonly attribute double percentageInlineSize;
    readonly attribute double percentageBlockSize;

    readonly attribute double? blockFragmentationOffset;
    readonly attribute BlockFragmentationType blockFragmentationType;

    readonly attribute any data;
};

dictionary LayoutConstraintsOptions {
    double availableInlineSize = 0;
    double availableBlockSize = 0;

    double fixedInlineSize;
    double fixedBlockSize;

    double percentageInlineSize;
    double percentageBlockSize;

    double blockFragmentationOffset;
    BlockFragmentationType blockFragmentationType = "none";

    any data;
};

enum BlockFragmentationType { "none", "page", "column", "region" };

LayoutConstraints 객체는 레이아웃 메서드에 전달되며, 현재 레이아웃이 내부에서 레이아웃을 수행하기 위한 모든 제약 조건을 나타냅니다. 또한 자식 레이아웃에 사용 가능한 공간 정보를 전달하는 데 사용됩니다.

LayoutConstraints 객체는 availableInlineSizeavailableBlockSize 속성을 가집니다. 이는 레이아웃이 준수해야 하는 LayoutFragment사용 가능한 공간을 나타냅니다.

참고: 일부 레이아웃은 이 크기를 초과하는 LayoutFragment를 생성해야 할 수 있습니다. 예를 들어 대체 요소 등입니다. 부모 레이아웃은 이러한 상황을 예상하고 적절하게 처리해야 합니다.

부모 레이아웃현재 레이아웃이 특정 크기를 정확히 갖도록 요구할 수 있습니다. fixedInlineSize 또는 fixedBlockSize가 지정된 경우 현재 레이아웃은 해당 방향에서 지정된 크기로 LayoutFragment를 생성해야 합니다.

레이아웃 알고리즘은 인라인 방향의 여분 공간을 flexbox와 유사하게 분배합니다. 자식 레이아웃 제약 조건을 생성하여 자식이 고정 인라인 크기를 갖도록 지정합니다.
registerLayout('flex-distribution-like', class {
    *intrinsicSizes(children, edges, styleMap) {
      const childrenSizes = yield children.map((child) => {
          return child.intrinsicSizes();
      });

      const maxContentSize = childrenSizes.reduce((sum, childSizes) => {
          return sum + childSizes.maxContentSize;
      }, 0) + edges.all.inline;

      const minContentSize = childrenSizes.reduce((max, childSizes) => {
          return sum + childSizes.minContentSize;
      }, 0) + edges.all.inline;

      return {maxContentSize, minContentSize};
    }

    *layout(children, edges, constraints, styleMap) {

        const availableInlineSize =
            constraints.fixedInlineSize - edges.all.inline;
        const availableBlockSize =
            (constraints.fixedInlineSize || Infinity) - edges.all.block;

        const childConstraints = { availableInlineSize, availableBlockSize };

        const unconstrainedChildFragments = yield children.map((child) => {
            return child.layoutNextFragment(childConstraints);
        });

        const unconstrainedSizes = [];
        const totalSize = unconstrainedChildFragments.reduce((sum, fragment, i) => {
            unconstrainedSizes[i] = fragment.inlineSize;
            return sum + fragment.inlineSize;
        }, 0);

        // 자식들 사이에 여분 공간을 분배합니다.
        const remainingSpace = Math.max(0, inlineSize - totalSize);
        const extraSpace = remainingSpace / children.length;

        const childFragments = yield children.map((child, i) => {
            return child.layoutNextFragment({
                fixedInlineSize: unconstrainedSizes[i] + extraSpace,
                availableBlockSize
            });
        });

        // 프래그먼트의 위치를 지정합니다.
        let inlineOffset = 0;
        let maxChildBlockSize = 0;
        for (let fragment of childFragments) {
            fragment.inlineOffset = inlineOffset;
            fragment.blockOffset = edges.all.blockStart;

            inlineOffset += fragment.inlineSize;
            maxChildBlockSize = Math.max(maxChildBlockSize, fragment.blockSize);
        }

        return {
            autoBlockSize: maxChildBlockSize + edges.all.block,
            childFragments,
        };
    }
});

LayoutConstraints 객체는 percentageInlineSizepercentageBlockSize 속성을 가집니다. 이는 레이아웃을 수행할 때 퍼센트 값을 해석할 기준이 되는 크기를 나타냅니다.

LayoutConstraints 객체는 blockFragmentationType 속성을 가집니다. 현재 레이아웃은 가능하다면 LayoutFragmentblockFragmentationOffset에서 프래그먼트화해야 합니다.

현재 레이아웃blockFragmentationType에 따라 LayoutChild를 프래그먼트하지 않을 수도 있습니다. 예를 들어, 자식에 break-inside: avoid-page;와 같은 속성이 있을 경우입니다.

사용자 에이전트가 레이아웃 제약 객체 생성sizingMode, box|, internalLayoutConstraints에 대해 수행하려고 할 때, 다음 단계를 실행해야 합니다:
  1. sizingMode"block-like"인 경우:

    1. fixedInlineSizeboxborder-box 인라인 크기로 계산합니다(box의 작성 모드 기준, 블록 컨테이너와 동일하게).

    2. fixedBlockSizebox블록 크기auto이면 null로, 아니면 블록 컨테이너와 동일하게 border-box 블록 크기로 계산합니다.

    3. 다음과 같이 새 LayoutConstraints 객체를 반환합니다:

  2. sizingMode"manual"인 경우:

    1. 다음과 같이 새 LayoutConstraints 객체를 반환합니다:

3.5. 분할 및 프래그먼트화

[Exposed=LayoutWorklet]
interface ChildBreakToken {
    readonly attribute BreakType breakType;
    readonly attribute LayoutChild child;
};

[Exposed=LayoutWorklet]
interface BreakToken {
    readonly attribute sequence<ChildBreakToken> childBreakTokens;
    readonly attribute any data;
};

dictionary BreakTokenOptions {
    sequence<ChildBreakToken> childBreakTokens;
    any data = null;
};

enum BreakType { "none", "line", "column", "page", "region" };

LayoutChild 는 여러 개의 LayoutFragment를 생성할 수 있습니다. LayoutChildblockFragmentationType이 none이 아닐 경우 블록 방향에서 프래그먼트될 수 있습니다. 또한 인라인 레벨 콘텐츠를 나타내는 LayoutChildlayout options' childDisplay(layoutOptions로 설정됨)가 "normal"일 때 줄 단위로 프래그먼트화될 수 있습니다.

이후의 LayoutFragment는 이전 LayoutFragmentbreakToken을 사용하여 생성됩니다. 이는 자식 레이아웃ChildBreakToken에 인코딩된 지점에서 LayoutFragment를 생성하도록 지시합니다.

이 예제는 자식 프래그먼트를 일정 줄 수만큼 들여쓰기하는 간단한 레이아웃을 보여줍니다.

이 예제는 LayoutFragment의 이전 breakToken을 사용하여 LayoutChild의 다음 프래그먼트를 생성하는 방법도 보여줍니다.

또한 BreakToken을 사용하여 LayoutConstraintsblockFragmentationType을 준수하는 방법을 보여줍니다. 이전 BreakToken에서 레이아웃을 재개합니다. FragmentResultOptions를 반환하며, breakToken을 사용해 레이아웃을 재개할 수 있습니다.

registerLayout('indent-lines', class {
    static layoutOptions = {childDisplay: 'normal'};
    static inputProperties = ['--indent', '--indent-lines'];

    *layout(children, edges, constraints, styleMap, breakToken) {

        // (내부) 사용 가능한 크기를 결정합니다.
        const availableInlineSize =
            constraints.fixedInlineSize - edges.all.inline;
        const availableBlockSize =
            (constraints.fixedBlockSize || Infinity) - edges.all.block;

        // 들여쓸 줄 수와 들여쓰기 값을 결정합니다.
        const indent = resolveLength(constraints, styleMap.get('--indent'));
        let lines = styleMap.get('--indent-lines').value;

        const childFragments = [];

        let childBreakToken = null;
        if (breakToken) {
            childBreakToken = breakToken.childBreakTokens[0];

            // 이미 프래그먼트를 생성한 자식들을 제거합니다.
            children.splice(0, children.indexOf(childBreakToken.child));
        }

        let blockOffset = edges.all.blockStart;
        let child = children.shift();
        while (child) {
            const shouldIndent = lines-- > 0;

            // 들여쓰기 값을 적용하여 인라인 크기를 조정합니다.
            const childAvailableInlineSize = shouldIndent ?
                availableInlineSize - indent : availableInlineSize;

            const childConstraints = {
                availableInlineSize: childAvailableInlineSize,
                availableBlockSize,
                percentageInlineSize: availableInlineSize,
                blockFragmentationType: constraints.blockFragmentationType,
            };

            const fragment = yield child.layoutNextFragment(childConstraints,
                                                            childBreakToken);
            childFragments.push(fragment);

            // 프래그먼트의 위치를 지정합니다.
            fragment.inlineOffset = shouldIndent ?
                edges.all.inlineStart + indent : edges.all.inlineStart;
            fragment.blockOffset = blockOffset;
            blockOffset += fragment.blockSize;

            // 블록 프래그먼트 한계를 초과했는지 확인합니다.
            if (constraints.blockFragmentationType != 'none' &&
                blockOffset > constraints.blockSize) {
                break;
            }

            if (fragment.breakToken) {
                childBreakToken = fragment.breakToken;
            } else {
                // 프래그먼트에 break token이 없으면 다음 자식으로 이동합니다.
                child = children.shift();
                childBreakToken = null;
            }
        }

        const autoBlockSize = blockOffset + edges.all.blockEnd;

        // 프래그먼트를 반환합니다.
        const result = {
            autoBlockSize,
            childFragments: childFragments,
        }

        if (childBreakToken) {
            result.breakToken = {
                childBreakTokens: [childBreakToken],
            };
        }

        return result;
    }
});

3.6. 엣지

[Exposed=LayoutWorklet]
interface LayoutEdgeSizes {
  readonly attribute double inlineStart;
  readonly attribute double inlineEnd;

  readonly attribute double blockStart;
  readonly attribute double blockEnd;

  // Convenience attributes for the sum in one direction.
  readonly attribute double inline;
  readonly attribute double block;
};

[Exposed=LayoutWorklet]
interface LayoutEdges {
  readonly attribute LayoutEdgeSizes border;
  readonly attribute LayoutEdgeSizes scrollbar;
  readonly attribute LayoutEdgeSizes padding;

  readonly attribute LayoutEdgeSizes all;
};

LayoutEdges 객체는 레이아웃 메서드에 전달됩니다. 이는 현재 레이아웃되는 박스의 박스 모델 엣지의 크기를 나타냅니다.

LayoutEdgesborder, scrollbar, padding 속성을 가집니다. 각각은 해당 엣지의 너비를 의미합니다.

LayoutEdges에는 all 속성이 있습니다. 이 속성은 border, scrollbar, padding 엣지의 합입니다.

LayoutEdgeSizes 객체는 각 추상 방향별(즉, inlineStart, inlineEnd, blockStart, blockEnd) CSS 픽셀 단위의 엣지 너비를 나타냅니다.

inline, block 속성은 LayoutEdgeSizes 객체에서 각 방향 합계에 대한 편의 속성입니다.

이 예시는 CSS로 스타일된 노드와 해당 LayoutEdges가 포함할 수 있는 내용을 보여줍니다.
<style>
.container {
  width: 50px;
  height: 50px;
}

.box {
  display: layout(box-edges);

  padding: 10%;
  border: solid 2px;
  overflow-y: scroll;
}
</style>

<div class="container">
  <div class="box"></div>
</div>
registerLayout('box-edges', class {
    *layout(children, edges, constraints, styleMap, breakToken) {
        edges.padding.inlineStart; // 5 (10% * 50px = 5px)
        edges.border.blockEnd; // 2
        edges.scrollbar.inlineEnd; // UA에 따라, 0 또는 0보다 클 수 있음(예: 16)
        edges.all.block; // 14 (2 + 5 + 5 + 2)
    }
}

4. 다른 모듈과의 상호작용

이 섹션은 다른 CSS 모듈이 CSS Layout API와 어떻게 상호작용하는지 설명합니다.

4.1. 크기 지정

사용자 에이전트는 LayoutConstraints 객체를 사용하여 현재 레이아웃에 프래그먼트의 원하는 크기를 전달해야 합니다.

사용자 에이전트가 박스의 크기를 강제로 지정하려면 fixedInlineSizefixedBlockSize 속성을 사용할 수 있습니다.

레이아웃 API 컨테이너layout options' sizing 값(클래스의 layoutOptions로 설정됨)에 따라 다양한 방식으로 크기 정보를 전달받을 수 있습니다.

layout options' sizing 값이 "block-like"이면, 레이아웃 API 컨테이너에 전달되는 LayoutConstraints는 다음과 같습니다:

layout options' sizing 값이 "manual"이면, 사용자 에이전트는 미리 fixedInlineSize 및/또는 fixedBlockSize 값을 계산하지 않아야 하며, 서식 컨텍스트에 의해 특정 크기로 강제되는 경우를 제외합니다. 예:

참고: 아래 예시에서 레이아웃 API 컨테이너의 인라인 크기는 50으로 설정됨.
<style>
  #container {
    width: 100px;
    height: 100px;
    box-sizing: border-box;
    padding: 5px;
  }
  #layout-api {
    display: layout(foo);
    margin: 0 20px;
  }
</style>
<div id="container">
  <div id="layout-api"></div>
</div>

4.1.1. 포지셔닝된 레이아웃 크기 지정

레이아웃 API 컨테이너가 플로우 밖 포지셔닝된 경우, 사용자 에이전트는 CSS Positioned Layout 3 §8.1 절의 절대/고정 포지션 비-대체 요소 너비, CSS Positioned Layout 3 §8.3 절의 절대/고정 포지션 비-대체 요소 높이의 포지셔닝 크기 방정식을 반드시 해결하고, 적절한 fixedInlineSizefixedBlockSize를 설정해야 합니다.

참고: 아래 예시에서 레이아웃 API 컨테이너의 인라인 및 블록 크기는 80으로 고정됨.
<style>
  #container {
    position: relative;
    width: 100px;
    height: 100px;
  }
  #layout-api {
    display: layout(foo);
    top: 10px;
    bottom: 10px;
    left: 10px;
    right: 10px;
    position: absolute;
  }
</style>
<div id="container">
  <div id="layout-api"></div>
</div>

4.2. 포지셔닝

이 명세 레벨에서는 모든 포지셔닝은 사용자 에이전트에 의해 처리됩니다.

결과적으로:

참고: 아래 예시에서
<style>
  #container {
    display: layout(foo);
    position: relative; /* container는 포함 블록 */
    width: 100px;
    height: 100px;
  }
  #child-relative {
    position: relative;
    left: 5px;
    top: 10px;
  }
</style>
<div id="container">
  <div id="child-relative"></div>
  <div id="child-absolute"></div>
</div>

4.3. 오버플로우

스크롤 가능한 오버플로우는 이 명세 레벨에서 레이아웃 API 컨테이너에 대해 사용자 에이전트가 처리합니다.

레이아웃 API 컨테이너는 블록 컨테이너와 동일하게 스크롤 가능한 오버플로우를 계산해야 합니다.

저자의 레이아웃 API 컨테이너가 프래그먼트를 스크롤 가능한 오버플로우 영역에 배치하더라도, 상대 포지셔닝 또는 변환이 프래그먼트를 이동시켜 오버플로우가 발생하지 않을 수 있습니다.

4.4. 프래그먼트화

부모 레이아웃현재 레이아웃프래그먼트화를 요청할 수 있으며, blockFragmentationTypeblockFragmentationOffset 을 설정합니다.

예를 들어 [css-multicol-1] 레이아웃은 blockFragmentationType"column"으로 설정하고, blockFragmentationOffset 을 자식이 프래그먼트화되어야 하는 위치로 설정합니다.

4.5. 정렬

레이아웃 API 컨테이너의 첫/마지막 기준선(baseline) 집합은 블록 컨테이너와 동일하게 생성됩니다(참고: CSS Box Alignment 3 §9.1 박스 기준선 결정 방법). 단, 인플로우 자식의 순서는 문서 순서가 아니라 레이아웃 메서드(즉 childFragments)에서 반환된 순서에 따라야 합니다.

참고: 향후 명세 레벨에서는 저자가 직접 기준선을 정의할 수 있도록 기능이 추가될 예정입니다. 예시:

기준선 정보를 LayoutChild에서 조회하는 방법:

const fragment = yield child.layoutNextFragment({
  fixedInlineSize: availableInlineSize,
  baselineRequests: ['alphabetic', 'middle'],
});
fragment.baselines.get('alphabetic') === 25 /* 혹은 기타 값 */;

기준선 정보를 부모 레이아웃제공하는 방법:

registerLayout('baseline-producing', class {
  *layout(children, edges, constraints, styleMap) {
    const result = {baselines: {}};

    for (let baselineRequest of constraints.baselineRequests) {
      // baselineRequest === 'alphabetic', 혹은 기타 값.
      result.baselines[baselineRequest] = 25;
    }

    return result;
  }
});

5. 레이아웃

이 섹션에서는 CSS Layout API가 사용자 에이전트의 레이아웃 엔진과 어떻게 상호작용하는지 설명합니다.

5.1. 개념

레이아웃 정의구조체로, 저자가 정의한 레이아웃(layout() 함수로 참조 가능)에 대해 LayoutWorkletGlobalScope가 필요로 하는 정보를 설명합니다. 다음을 포함합니다:

문서 레이아웃 정의구조체로, 저자가 정의한 레이아웃(layout() 함수로 참조 가능)에 대해 문서가 필요로 하는 정보를 설명합니다. 다음을 포함합니다:

5.2. 레이아웃 무효화

박스에는 레이아웃 유효 플래그가 연관되어 있습니다. 이 플래그는 layout-valid 또는 layout-invalid가 될 수 있으며, 처음에는 layout-invalid로 설정됩니다.

박스에는 내재적 크기 유효 플래그가 연관되어 있습니다. 이 플래그는 intrinsic-sizes-valid 또는 intrinsic-sizes-invalid가 될 수 있으며, 처음에는 intrinsic-sizes-invalid로 설정됩니다.

사용자 에이전트가 레이아웃 함수 무효화box에 대해 실행하려고 할 때, 다음 단계를 반드시 수행해야 합니다:
  1. layoutFunctionbox의 계산된 스타일에서 display 속성의 layout() 함수로 설정합니다(존재하는 경우). 값이 다른 타입(예: grid)이면 모든 단계를 중단합니다.

  2. namelayoutFunction의 첫 번째 인자로 설정합니다.

  3. documentDefinition문서 레이아웃 정의 가져오기의 결과로 name을 인자로 얻습니다.

    문서 레이아웃 정의 가져오기가 실패하거나 documentDefinition"invalid"이면 모든 단계를 중단합니다.

  4. inputPropertiesdocumentDefinitioninput properties로 설정합니다.

  5. childInputPropertiesdocumentDefinitionchild input properties로 설정합니다.

  6. inputProperties의 각 property에 대해, 해당 property계산된 값이 변경되었다면, 레이아웃 유효 플래그layout-invalid로, 내재적 크기 유효 플래그intrinsic-sizes-invalid로 설정합니다.

  7. childInputProperties의 각 property에 대해, 해당 property계산된 값이 변경되었다면, 레이아웃 유효 플래그layout-invalid로, 내재적 크기 유효 플래그intrinsic-sizes-invalid로 설정합니다.

레이아웃 함수 무효화는 박스의 계산된 스타일이 재계산될 때나, 해당 박스의 자식의 계산된 스타일이 재계산될 때 반드시 실행되어야 합니다.

LayoutChild로 표현되는 자식 박스박스 트리에 추가/제거되거나, 레이아웃이 무효화(계산된 스타일 변경, 또는 후손 변경 등)되어 해당 무효화가 박스 트리 상위로 전파되어야 할 때, 현재 레이아웃 유효 플래그layout-invalid로, 현재 내재적 크기 유효 플래그intrinsic-sizes-invalid로 설정합니다.

레이아웃 API 컨테이너의 계산된 스타일이 변경되고, 이 변경이 LayoutEdges 객체 내부의 값에 영향을 주는 경우, 박스의 레이아웃 유효 플래그layout-invalid로, 내재적 크기 유효 플래그intrinsic-sizes-invalid로 설정합니다.

계산된 스타일 변경이 LayoutConstraints 객체 내부의 값에 영향을 주는 경우, 박스의 내재적 크기 유효 플래그intrinsic-sizes-invalid로 설정합니다.

참고: LayoutConstraints 객체는 오직 레이아웃 함수에만 전달되므로, 내재적 크기를 무효화할 필요는 없습니다.

참고: 다음과 같은 속성은 LayoutEdges 객체를 변경할 수 있습니다:

다음과 같은 속성은 LayoutConstraints 객체를 변경할 수 있습니다:

참고: 이 내용은 CSS Layout API와 관련된 레이아웃 무효화만 설명합니다. 모든 박스는 개념적으로 레이아웃 유효 플래그를 가지며, 이러한 변경은 박스 트리 전체로 전파됩니다.

5.3. 레이아웃 워클릿

layoutWorklet 속성은 레이아웃과 관련된 모든 클래스에 대해 책임지는 Worklet에 접근할 수 있게 해줍니다.

layoutWorkletworklet global scope typeLayoutWorkletGlobalScope입니다.

partial interface CSS {
    [SameObject] readonly attribute Worklet layoutWorklet;
};

LayoutWorkletGlobalScopelayoutWorklet의 글로벌 실행 컨텍스트입니다.

[Global=(Worklet,LayoutWorklet),Exposed=LayoutWorklet]
interface LayoutWorkletGlobalScope : WorkletGlobalScope {
    void registerLayout(DOMString name, VoidFunction layoutCtor);
};

5.4. 레이아웃 등록

[Exposed=LayoutWorklet]
dictionary LayoutOptions {
  ChildDisplayType childDisplay = "block";
  LayoutSizingMode sizing = "block-like";
};

[Exposed=LayoutWorklet]
enum ChildDisplayType {
    "block",
    "normal",
};

[Exposed=LayoutWorklet]
enum LayoutSizingMode {
    "block-like",
    "manual",
};

document에는 형태의 문서 레이아웃 정의들이 있습니다. 처음에는 이 맵이 비어 있으며, registerLayout(name, layoutCtor) 가 호출될 때 채워집니다.

LayoutWorkletGlobalScope 에도 형태의 레이아웃 정의들이 있습니다. 처음에는 이 맵이 비어 있으며, registerLayout(name, layoutCtor) 가 호출될 때 채워집니다.

박스레이아웃 API 컨테이너를 나타내는 각 박스는 형태의 레이아웃 클래스 인스턴스를 가집니다. 처음에는 이 맵이 비어 있으며, 사용자 에이전트가 내재적 크기 결정 또는 프래그먼트 생성을 박스에 대해 호출할 때 채워집니다.

registerLayout(name, layoutCtor) 메서드가 호출되면, 사용자 에이전트는 다음 단계를 반드시 수행해야 합니다:
  1. name이 빈 문자열이면, throw TypeError를 발생시키고 모든 단계를 중단합니다.

  2. layoutDefinitionMapLayoutWorkletGlobalScope레이아웃 정의 맵으로 설정합니다.

  3. layoutDefinitionMap[name]이 존재하면, throw "InvalidModificationError" DOMException을 발생시키고 모든 단계를 중단합니다.

  4. inputProperties를 빈 sequence<DOMString>로 설정합니다.

  5. inputPropertiesIterableGet(layoutCtor, "inputProperties")의 결과로 설정합니다.

  6. inputPropertiesIterable이 undefined가 아니면, inputProperties변환 결과로 설정합니다. 변환 중 예외가 throw 되면 예외를 다시 throw하고 모든 단계를 중단합니다.

    참고: input properties getter에서 제공되는 CSS 속성 리스트는 커스텀 또는 네이티브 CSS 속성일 수 있습니다.

    참고: 해당 리스트에는 shorthand도 포함될 수 있습니다.

    참고: 레이아웃 클래스가 향후 호환성을 위해, 리스트에 현재 사용자 에이전트에서 유효하지 않은 속성도 포함할 수 있습니다. 예: margin-bikeshed-property 등.

  7. childInputProperties를 빈 sequence<DOMString>로 설정합니다.

  8. childInputPropertiesIterableGet(layoutCtor, "childInputProperties")의 결과로 설정합니다.

  9. childInputPropertiesIterable이 undefined가 아니면, childInputProperties변환 결과로 설정합니다. 변환 중 예외가 throw 되면 예외를 다시 throw하고 모든 단계를 중단합니다.

  10. layoutOptionsValueGet(layoutCtor, "layoutOptions")의 결과로 설정합니다.

  11. layoutOptions변환 결과로 설정합니다. 변환 중 예외가 throw 되면 예외를 다시 throw하고 모든 단계를 중단합니다.

  12. IsConstructor(layoutCtor)가 false이면 throw TypeError를 발생시키고 모든 단계를 중단합니다.

  13. prototypeGet(layoutCtor, "prototype")의 결과로 설정합니다.

  14. Type(prototype)이 Object가 아니면 throw TypeError를 발생시키고 모든 단계를 중단합니다.

  15. intrinsicSizesGet(prototype, "intrinsicSizes")의 결과로 설정합니다.

  16. IsCallable(intrinsicSizes)가 false이면 throw TypeError를 발생시키고 모든 단계를 중단합니다.

  17. intrinsicSizes[[FunctionKind]] 내부 슬롯이 "generator"가 아니면 throw TypeError를 발생시키고 모든 단계를 중단합니다.

  18. layoutGet(prototype, "layout")의 결과로 설정합니다.

  19. IsCallable(layout)이 false이면 throw TypeError를 발생시키고 모든 단계를 중단합니다.

  20. layout[[FunctionKind]] 내부 슬롯이 "generator"가 아니면 throw TypeError를 발생시키고 모든 단계를 중단합니다.

  21. definition을 새로운 레이아웃 정의로 생성하며:

  22. Set layoutDefinitionMap[name] = definition

  23. Queue a task로 다음 단계 실행:

    1. documentLayoutDefinitionMap을 연결된 document문서 레이아웃 정의 으로 설정

    2. documentDefinition을 새로운 문서 레이아웃 정의로 생성하며:

    3. documentLayoutDefinitionMap[name]이 존재하면 다음 단계 실행:

      1. existingDocumentDefinitionget documentLayoutDefinitionMap[name]로 설정

      2. existingDocumentDefinition"invalid"이면 모든 단계 중단

      3. existingDocumentDefinitiondocumentDefinition이 동일하지 않으면(input properties, child input properties, layout options이 다르면):

        Set documentLayoutDefinitionMap[name] = "invalid"

        동일 클래스가 서로 다른 inputProperties, childInputProperties, layoutOptions로 등록되었음을 디버깅 콘솔에 에러로 기록

    4. 그 외에는 set documentLayoutDefinitionMap[name] = documentDefinition

참고: 클래스 구조 예시는 다음과 같습니다:
class MyLayout {
    static get inputProperties() { return ['--foo']; }
    static get childrenInputProperties() { return ['--bar']; }
    static get layoutOptions() {
      return {childDisplay: 'normal', sizing: 'block-like'}
    }

    *intrinsicSizes(children, edges, styleMap) {
        // 내재적 크기 코드
    }

    *layout(children, edges, constraints, styleMap, breakToken) {
        // 레이아웃 코드
    }
}

5.5. 레이아웃 엔진

이 API는 현재 이터러블 제너레이터 형태입니다. 구현 경험과 웹 개발자 경험에 따라 promise 기반 API로 변경될 수도 있습니다. 각 방식의 장단점은 다음과 같습니다.

프라미스 기반

제너레이터 기반

5.5.1. 요청 객체

[Exposed=LayoutWorklet]
interface IntrinsicSizesRequest {
};

[Exposed=LayoutWorklet]
interface LayoutFragmentRequest {
};

typedef (IntrinsicSizesRequest or LayoutFragmentRequest)
    LayoutFragmentRequestOrIntrinsicSizesRequest;

IntrinsicSizesRequest 객체는 내부 슬롯을 가집니다:

LayoutFragmentRequest 객체는 내부 슬롯을 가집니다:


저자가 제공한 레이아웃 클래스의 layout 메서드와 intrinsicSizes 메서드는 일반 자바스크립트 함수가 아니라 제너레이터 함수입니다. 이는 사용자 에이전트가 비동기 및 병렬 레이아웃 엔진을 지원할 수 있도록 하기 위함입니다.

저자가 layoutNextFragment() 메서드를 LayoutChild에서 호출하면, 사용자 에이전트는 저자의 코드에 즉시 LayoutFragment를 동기적으로 반환하지 않습니다. 대신 LayoutFragmentRequest를 반환합니다. 이는 저자에게 완전히 불투명한 객체이지만, 내부 슬롯에는 layoutNextFragment() 호출에 대한 정보가 들어있습니다.

레이아웃 제너레이터 객체에서 LayoutFragmentRequest들이 yield될 때, 사용자 에이전트의 레이아웃 엔진은 해당 알고리즘을 다른 작업과 병렬로, 또는 다른 실행 스레드에서 비동기로 실행할 수 있습니다. 엔진에서 LayoutFragment들이 생성되면, 사용자 에이전트는 결과 LayoutFragment를 제너레이터 객체에 "tick"합니다.

intrinsicSizes() 메서드도 동일하게 동작합니다.

자바스크립트로 작성된 예시 레이아웃 엔진은 아래와 같습니다.
class LayoutEngine {
  // 이 함수는 박스 트리의 루트, LayoutConstraints 객체, BreakToken(예: 인쇄용 페이지네이션)을 받아 LayoutFragment를 생성합니다.
  layoutEntry(rootBox, rootPageConstraints, breakToken) {
    return layoutFragment({
      box: rootBox,
      layoutConstraints: rootPageConstraints,
      breakToken: breakToken,
    });
  }

  // 이 함수는 LayoutFragmentRequest를 받아서 해당 레이아웃 알고리즘을 호출하여 LayoutFragment를 생성합니다.
  layoutFragment(fragmentRequest) {
    const box = fragmentRequest.layoutChild;
    const algorithm = selectLayoutAlgorithmForBox(box);
    const fragmentRequestGenerator = algorithm.layout(
        fragmentRequest.layoutConstraints,
        box.children,
        box.styleMap,
        fragmentRequest.breakToken);

    let nextFragmentRequest = fragmentRequestGenerator.next();

    while (!nextFragmentRequest.done) {
      // 사용자 에이전트는 프래그먼트 생성을 병렬로(다른 스레드에서) 할 수도 있습니다. 이 예제는 순차적으로 동기 실행합니다.
      let fragments = nextFragmentRequest.value.map(layoutFragment);

      // 사용자 에이전트는 다른 작업(예: GC) 전 레이아웃을 yield할 수도 있음. 이 예제는 yield 없이 동기 실행.
      nextFragmentRequest = fragmentRequestGenerator.next(fragments);
    }

    return nextFragmentRequest.value; // 최종 LayoutFragment 반환
  }
}

5.6. 레이아웃 수행

// 저자 정의 layout() 메서드의 최종 반환값입니다.
dictionary FragmentResultOptions {
    double inlineSize = 0;
    double blockSize = 0;
    double autoBlockSize = 0;
    sequence<LayoutFragment> childFragments = [];
    any data = null;
    BreakTokenOptions breakToken = null;
};

dictionary IntrinsicSizesResultOptions {
    double maxContentSize;
    double minContentSize;
};

5.6.1. 내재적 크기 결정

내재적 크기 결정 알고리즘은 사용자 에이전트가 저자 정의 레이아웃에서 박스내재적 크기 정보를 쿼리하는 방법을 정의합니다.

참고: 내재적 크기 결정 알고리즘은 사용자 에이전트가 이전 호출 결과를 임의 개수 캐싱해서 재사용할 수 있게 허용합니다.

사용자 에이전트가 box, childBoxes에 대해 레이아웃 API 서식 컨텍스트내재적 크기 결정을 하려는 경우 반드시 다음 단계를 수행해야 합니다:
  1. layoutFunctionboxlayout() 계산값의 <display-inside>로 설정합니다.

  2. layoutFunction내재적 크기 유효 플래그intrinsic-sizes-valid인 경우 사용자 에이전트는 이전 호출의 내재적 크기를 사용할 수 있습니다. 이 경우 모든 단계를 중단하고 이전 값을 사용해도 됩니다.

  3. layoutFunction내재적 크기 유효 플래그intrinsic-sizes-valid로 설정합니다.

  4. namelayoutFunction의 첫 번째 인자로 설정합니다.

  5. documentDefinition문서 레이아웃 정의 가져오기 결과로 name을 인자로 받아 설정합니다.

    만약 문서 레이아웃 정의 가져오기가 실패하거나 documentDefinition"invalid"라면, boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  6. workletGlobalScope를 레이아웃 WorkletWorkletGlobalScopes 리스트에서 LayoutWorkletGlobalScope로 선택합니다.

    사용자 에이전트는 최소 2개 이상의 LayoutWorkletGlobalScopeWorkletGlobalScopes 리스트에서 반드시 생성/선택해야 합니다(메모리 제약이 없는 경우).

    참고: 저자가 글로벌 객체나 클래스에 재생성 불가 상태를 저장하지 않도록 보장하기 위함입니다.

    사용자 에이전트는 이 시점에 WorkletGlobalScope 생성을 해도 됩니다.

  7. 내재적 크기 콜백 호출name, box, childBoxes, workletGlobalScope로 실행합니다. (옵션으로 병렬로 수행 가능)

    참고: 병렬 수행 시, 해당 스레드에서 사용할 수 있는 layout worklet global scope를 선택해야 합니다.

사용자 에이전트가 내재적 크기 콜백 호출name, box, childBoxes, workletGlobalScope에 대해 하려는 경우, 반드시 다음 단계를 수행해야 합니다:
  1. definition레이아웃 정의 가져오기 결과로 name, workletGlobalScope를 인자로 받아 설정합니다.

    만약 레이아웃 정의 가져오기가 실패했다면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  2. layoutInstance레이아웃 클래스 인스턴스 가져오기 결과로 box, definition, workletGlobalScope를 인자로 받아 설정합니다.

    만약 레이아웃 클래스 인스턴스 가져오기가 실패했다면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  3. inputPropertiesdefinitioninput properties로 설정합니다.

  4. children을 새로운 리스트로 생성합니다.

  5. childBox에 대해 다음을 수행:

    1. layoutChild레이아웃 자식 가져오기 결과로 workletGlobalScope, name, childBox를 인자로 받아 설정합니다.

    2. Append layoutChildchildren에 추가합니다.

  6. edgesLayoutEdgeSizes로 생성하고, box의 모든 박스 모델 엣지계산값으로 채웁니다.

  7. styleMap을 새로운 StylePropertyMapReadOnly로 생성하고, boxinputProperties에 나열된 속성의 계산값만 담습니다.

    styleMapbox에 저장할지(layoutInstance와 유사하게) 고려할 수 있음.

  8. 이 시점에 children, styleMap이 이전 호출과 동일하면, 사용자 에이전트는 이전 호출의 내재적 크기를 재사용할 수 있습니다. 이 경우 캐시된 내재적 크기를 사용하고 모든 단계를 중단합니다.

  9. intrinsicSizesGeneratorFunctiondefinition내재적 크기 생성자 함수로 설정합니다.

  10. intrinsicSizesGeneratorInvoke(intrinsicSizesGeneratorFunction, layoutInstance, «children, edges, styleMap»)의 결과로 설정합니다.

    예외가 throw되면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  11. intrinsicSizesValuerun a generator(intrinsicSizesGenerator, "intrinsic-sizes")의 결과로 설정합니다.

    run a generator가 실패하면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  12. intrinsicSizes변환(intrinsicSizesValueIntrinsicSizesResultOptions) 결과로 설정합니다. 예외가 throw되면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  13. box내재적 크기를 다음과 같이 설정:

5.6.2. 프래그먼트 생성

프래그먼트 생성 알고리즘은 사용자 에이전트가 저자 정의 레이아웃에 대해 박스프래그먼트를 생성하는 방법을 정의합니다.

참고: 프래그먼트 생성 알고리즘은 사용자 에이전트가 이전 호출 결과를 임의 개수 캐싱해서 재사용할 수 있게 허용합니다.

사용자 에이전트가 box, childBoxes, internalLayoutConstraints, (옵션) internalBreakToken에 대해 레이아웃 API 서식 컨텍스트프래그먼트 생성을 하려는 경우, 반드시 다음 단계를 수행해야 합니다:
  1. layoutFunctionboxlayout() 계산값의 <display-inside>로 설정합니다.

  2. layoutFunction레이아웃 유효 플래그layout-valid이면, 사용자 에이전트는 이전 호출의 내재적 크기를 사용할 수 있습니다. 이 경우 모든 단계를 중단하고 이전 값을 사용해도 됩니다.

  3. layoutFunction레이아웃 유효 플래그layout-valid로 설정합니다.

  4. namelayoutFunction의 첫 번째 인자로 설정합니다.

  5. documentDefinition문서 레이아웃 정의 가져오기 결과로 name을 인자로 받아 설정합니다.

    만약 문서 레이아웃 정의 가져오기가 실패하거나 documentDefinition"invalid"라면, boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  6. workletGlobalScopeLayoutWorkletGlobalScope로 두고, layout Workletworklet의 WorkletGlobalScopes 목록에서 선택한다.

    사용자 에이전트는 목록 내에서 적어도 두 개 이상의 LayoutWorkletGlobalScope를 보유하고 선택할 의무가 있다. 단, 사용자 에이전트가 메모리 제약에 있는 경우는 예외이다.

    참고: 저자가 글로벌 객체나 클래스에 재생성할 수 없는 상태나 상태 저장에 의존하지 않도록 하기 위함이다.

    사용자 에이전트는 이 시점에서 layout Worklet에 대해 WorkletGlobalScope를 생성할 수도 있다.

  7. 레이아웃 콜백 호출name, box, childBoxes, internalLayoutConstraints, internalBreakToken, workletGlobalScope로 실행합니다. (옵션으로 병렬로 수행 가능)

    참고: 병렬 수행 시, 해당 스레드에서 사용할 수 있는 layout worklet global scope를 선택해야 합니다.

사용자 에이전트가 레이아웃 콜백 호출name, box, childBoxes, internalLayoutConstraints, internalBreakToken, workletGlobalScope에 대해 하려는 경우, 반드시 다음 단계를 수행해야 합니다:
  1. definition레이아웃 정의 가져오기 결과로 name, workletGlobalScope를 인자로 받아 설정합니다.

    만약 레이아웃 정의 가져오기가 실패했다면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  2. layoutInstance레이아웃 클래스 인스턴스 가져오기 결과로 box, definition, workletGlobalScope를 인자로 받아 설정합니다.

    만약 레이아웃 클래스 인스턴스 가져오기가 실패했다면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  3. sizingModedefinitionlayout optionssizing 값으로 설정합니다.

  4. inputPropertiesdefinitioninput properties로 설정합니다.

  5. children을 새로운 리스트로 생성합니다.

  6. childBox에 대해 다음을 수행:

    1. layoutChild레이아웃 자식 가져오기 결과로 workletGlobalScope, name, childBox를 인자로 받아 설정합니다.

    2. Append layoutChildchildren에 추가합니다.

  7. edgesLayoutEdgeSizes로 생성하고, box의 모든 박스 모델 엣지계산값으로 채웁니다.

  8. layoutConstraints레이아웃 제약 객체 생성(internalLayoutConstraints, box, sizingMode) 결과로 설정합니다.

  9. styleMap을 새로운 StylePropertyMapReadOnly로 생성하고, boxinputProperties에 나열된 속성의 계산값만 담습니다.

    styleMapbox에 저장할지(layoutInstance와 유사하게) 고려할 수 있음.

  10. breakTokeninternalBreakToken의 정보로 채워진 새 BreakToken로 생성합니다.

    internalBreakToken이 null이면 breakToken도 null로 합니다.

  11. 이 시점에 children, styleMap, layoutConstraints, breakToken이 이전 호출과 동일하면, 사용자 에이전트는 캐시된 프래그먼트를 재사용할 수 있습니다. 이 경우 캐시된 프래그먼트를 반환하고 모든 단계를 중단합니다.

  12. layoutGeneratorFunctiondefinition레이아웃 생성자 함수로 설정합니다.

  13. layoutGeneratorInvoke(layoutGeneratorFunction, layoutInstance, «children, edges, layoutConstraints, styleMap, breakToken»)의 결과로 설정합니다.

    예외가 throw되면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  14. fragmentValuerun a generator(layoutGenerator, "layout")의 결과로 설정합니다.

    run a generator가 실패하면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  15. fragment변환(fragmentValueFragmentResultOptions) 결과로 설정합니다. 예외가 throw되면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  16. childFragment에 대해 fragmentchildFragments리스트에서 다음을 수행:

    1. childFragment[[generator]] 내부 슬롯이 layoutGenerator와 같지 않으면 boxflow layout으로 폴백하고 모든 단계를 중단합니다.

  17. sizingMode"block-like"인 경우:

    • 다음 단계를 수행:

      1. inlineSizelayoutConstraintsfixedInlineSize로 설정합니다. ("block-like" sizing에서는 반드시 값이 설정되어야 함)

      2. blockSizeboxborder-box 블록 크기로(작성 모드 기준, 블록 컨테이너와 동일하게), fragmentautoBlockSize를 "내재적 높이"로 사용해 계산합니다.

    • 그 외(sizingMode"manual"인 경우):

      1. inlineSizefragmentinlineSize로 설정

      2. blockSizefragmentblockSize로 설정

  18. box에 대해 프래그먼트를 다음과 같이 반환:

5.6.3. 유틸리티 알고리즘

이 섹션은 내재적 크기 결정프래그먼트 생성 알고리즘에서 공통적으로 사용되는 알고리즘을 명세합니다.

사용자 에이전트가 name에 대해 문서 레이아웃 정의 가져오기를 하려면, 반드시 다음 단계를 수행합니다:
  1. documentLayoutDefinitionMap을 연결된 문서문서 레이아웃 정의 맵으로 설정합니다.

  2. documentLayoutDefinitionMap[name]이 존재하지 않으면, 실패를 반환하고 모든 단계를 중단합니다.

  3. get documentLayoutDefinitionMap[name]의 결과를 반환합니다.

사용자 에이전트가 name, workletGlobalScope에 대해 레이아웃 정의 가져오기를 하려면, 반드시 다음 단계를 수행합니다:
  1. layoutDefinitionMapworkletGlobalScope레이아웃 정의 맵으로 설정합니다.

  2. layoutDefinitionMap[name]이 존재하지 않으면, 다음 단계를 수행합니다:

    1. Queue a task로 다음 단계를 실행합니다:

      1. documentLayoutDefinitionMap을 연결된 문서문서 레이아웃 정의 맵으로 설정합니다.

      2. Set documentLayoutDefinitionMap[name] = "invalid"로 설정합니다.

      3. 사용자 에이전트는 모든 LayoutWorkletGlobalScope에 클래스가 등록되지 않았음을 디버깅 콘솔에 에러로 기록해야 합니다.

    2. 실패를 반환하고 모든 단계를 중단합니다.

  3. get layoutDefinitionMap[name]의 결과를 반환합니다.

사용자 에이전트가 box, definition, workletGlobalScope에 대해 레이아웃 클래스 인스턴스 가져오기를 하려면, 반드시 다음 단계를 수행합니다:
  1. layoutClassInstanceMapbox레이아웃 클래스 인스턴스 맵으로 설정합니다.

  2. layoutInstanceget layoutClassInstanceMap[workletGlobalScope]의 결과로 설정합니다. layoutInstance가 null이면, 다음 단계를 실행합니다:

    1. definitionconstructor valid flag가 false이면, 실패를 반환하고 모든 단계를 중단합니다.

    2. layoutCtordefinition클래스 생성자로 설정합니다.

    3. layoutInstanceConstruct(layoutCtor)의 결과로 설정합니다.

      construct가 예외를 발생시키면, definitionconstructor valid flag를 false로 설정하고, 실패를 반환하며 모든 단계를 중단합니다.

    4. Set layoutClassInstanceMap[workletGlobalScope] = layoutInstance로 설정합니다.

  3. layoutInstance를 반환합니다.

사용자 에이전트가 generator, generatorType에 대해 제너레이터 실행을 하려면, 반드시 다음 단계를 수행합니다:
  1. doneboolean으로 false로 초기화합니다.

  2. nextValue를 undefined로 설정합니다.

  3. donetrue가 될 때까지 다음 단계를 반복합니다:

    1. nextFunctionGet(generator, "next") 결과로 설정합니다.

    2. IsCallable(nextFunction)이 false이면 throw TypeError를 발생시키고 모든 단계를 중단합니다.

    3. nextResultInvoke(nextFunction, generator, «nextValue»)

      예외가 throw되면 실패를 반환하고 모든 단계를 중단합니다.

    4. Type(nextResult)이 Object가 아니면 throw TypeError를 발생시키고 모든 단계를 중단합니다.

    5. requestOrRequestsGet(nextResult|, "value") 결과로 설정합니다.

    6. doneGet(nextResult, "done") 결과로 설정합니다.

    7. GetMethod(requestOrRequests, @@iterable)가 존재하는 경우:

      1. results를 새로운 리스트로 설정합니다.

      2. requests변환(requestOrRequestssequence<LayoutFragmentRequestOrIntrinsicSizesRequest>) 결과로 설정합니다.

        예외가 throw되면 예외를 다시 throw하고 모든 단계를 중단합니다.

      3. request에 대해 다음을 수행:

        1. result제너레이터 결과 생성(request, generator, generatorType) 결과로 설정합니다.

        2. Append resultresults에 추가합니다.

      4. nextValueresults로 설정합니다.

      5. 계속 진행합니다.

    8. request변환(requestOrRequestsLayoutFragmentRequestOrIntrinsicSizesRequest) 결과로 설정합니다.

      예외가 throw되면 예외를 다시 throw하고 모든 단계를 중단합니다.

    9. result제너레이터 결과 생성(request, generator, generatorType) 결과로 설정합니다.

      제너레이터 결과 생성이 실패를 반환하면, 실패를 반환하고 모든 단계를 중단합니다.

    10. nextValueresult로 설정합니다.

    11. 계속 진행합니다.

    사용자 에이전트는 위 루프를 순서와 다르게 실행하거나 병렬로 실행할 수 있습니다. 하지만 requestsresults의 순서는 반드시 일관성을 유지해야 합니다.

    참고: 이는 사용자 에이전트가 적절한 레이아웃 알고리즘을 다른 스레드에서 실행하거나 비동기로 layout 작업을 분할할 수 있게 하기 위함입니다. 병렬 실행 시, 외부 루프는 교차 스레드 작업이 모두 끝날 때까지 기다려야 하며, 저자에게 부분 결과를 반환할 수 없습니다.

  4. Get(nextResult, "value")의 결과를 반환합니다.

사용자 에이전트가 request, generator, generatorType에 대해 제너레이터 결과 생성을 하려면, 반드시 다음 단계를 수행합니다:
  1. requestIntrinsicSizesRequest이면:

    1. layoutChildrequest의 내부 슬롯 [[layoutChild]]에서 조회합니다.

    2. boxlayoutChild의 내부 슬롯 [[box]]에서 조회합니다.

    3. box박스 트리에 연결되어 있지 않으면, 실패를 반환하고 모든 단계를 중단합니다.

      참고: 저자가 이전 호출에서 LayoutChild를 유지하고 있을 수 있습니다. 박스는 박스 트리에 한번만 연결되어 재사용되지 않는 것으로 가정합니다.

    4. internalIntrinsicSizes를 사용자 에이전트가 boxborder box min/max content 기여도 계산 결과로 설정합니다.

    5. 내재적 크기 객체 생성(request, internalIntrinsicSizes) 결과를 반환합니다.

  2. requestLayoutFragmentRequest이고 generatorType"layout"이면:

    1. layoutChildrequest의 내부 슬롯 [[layoutChild]]에서 조회합니다.

    2. boxlayoutChild의 내부 슬롯 [[box]]에서 조회합니다.

    3. box박스 트리에 연결되어 있지 않으면, 실패를 반환하고 모든 단계를 중단합니다.

      참고: 저자가 이전 호출에서 LayoutChild를 유지하고 있을 수 있습니다. 박스는 박스 트리에 한번만 연결되어 재사용되지 않는 것으로 가정합니다.

    4. childLayoutConstraintsrequest의 내부 슬롯 [[layoutConstraints]]에서 조회합니다.

    5. childBreakTokenrequest의 내부 슬롯 [[breakToken]]에서 조회합니다.

    6. internalFragment를 사용자 에이전트가 box, childLayoutConstraints, childBreakToken에 기반해 프래그먼트를 생성한 결과로 설정합니다.

    7. 레이아웃 프래그먼트 생성(generator, request, internalFragment) 결과를 반환합니다.

  3. 위 분기가 모두 해당되지 않으면 실패를 반환합니다.

6. 보안 고려사항

이 기능들로 인해 알려진 보안 이슈는 없습니다.

7. 개인정보 보호 고려사항

이 기능들로 인해 알려진 개인정보 보호 이슈는 없습니다.

적합성

문서 규칙

적합성 요구사항은 설명적 단언과 RFC 2119 용어의 조합으로 표현됩니다. 규범적인 부분에서 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”이라는 핵심 단어는 RFC 2119에 설명된 대로 해석해야 합니다. 그러나 가독성을 위해, 이 단어들은 이 명세서에서 모두 대문자로 나타나지 않습니다.

이 명세서의 모든 텍스트는 명시적으로 비규범적인 것으로 표시된 섹션, 예시, 그리고 참고를 제외하고는 규범적입니다. [RFC2119]

이 명세서의 예시는 “예를 들어”라는 문구로 시작하거나 class="example"로 규범적 텍스트와 구분되어 있습니다. 예시:

이것은 정보 제공용 예시입니다.

정보 제공용 참고는 “참고”라는 단어로 시작하며, class="note"로 규범적 텍스트와 구분되어 있습니다. 예시:

참고, 이것은 정보 제공용 참고입니다.

권고 사항은 특별한 주의를 환기하기 위해 스타일링된 규범적 섹션이며, <strong class="advisement">로 다른 규범적 텍스트와 구분되어 있습니다. 예시: UA는 접근 가능한 대안을 반드시 제공해야 합니다.

적합성 클래스

이 명세서에 대한 적합성은 세 가지 적합성 클래스로 정의됩니다:

스타일 시트
CSS 스타일 시트.
렌더러
UA는 스타일 시트의 의미를 해석하고, 그것을 사용하는 문서를 렌더링합니다.
저작 도구
UA는 스타일 시트를 작성합니다.

스타일 시트가 이 명세서에 적합하려면, 이 모듈에서 정의된 구문을 사용하는 모든 선언이 일반 CSS 문법과 각 기능별 개별 문법에 따라 유효해야 합니다.

렌더러가 이 명세서에 적합하려면, 해당 스타일 시트를 관련 명세서에서 정의한 대로 해석하는 것과 더불어, 이 명세서에서 정의된 모든 기능을 올바르게 파싱하고 문서를 그에 따라 렌더링해야 합니다. 그러나 UA가 장치의 한계로 인해 문서를 올바르게 렌더링하지 못하더라도 UA가 비적합한 것은 아닙니다. (예를 들어, UA는 흑백 모니터에서 색상을 렌더링할 필요는 없습니다.)

저작 도구가 이 명세서에 적합하려면, 일반 CSS 문법과 이 모듈의 각 기능별 개별 문법에 따라 문법적으로 올바른 스타일 시트를 작성해야 하며, 이 모듈에 설명된 모든 스타일 시트 적합성 요구사항을 충족해야 합니다.

부분 구현

작성자가 미래 호환 파싱 규칙을 활용해 대체 값을 지정할 수 있도록, CSS 렌더러는 사용 가능한 수준의 지원이 없는 모든 at-규칙, 속성, 속성값, 키워드 및 기타 구문적 구조를 무효로 간주하고 적절하게 무시해야 합니다. 특히, 사용자 에이전트는 지원되지 않는 구성 요소 값을 선택적으로 무시하고 지원되는 값만 단일 다중값 속성 선언에서 적용해서는 안 됩니다: 어떤 값이라도 무효(지원되지 않는 값이어야 함)로 간주되면, CSS는 전체 선언이 무시되어야 함을 요구합니다.

불안정 및 독점 기능의 구현

미래의 안정적인 CSS 기능과 충돌을 피하기 위해, CSSWG는 베스트 프랙티스를 따를 것을 권장하며, 불안정한 기능 및 독점 확장 구현 시 이를 권장합니다.

비실험적 구현

명세서가 후보 권고 단계에 도달하면, 비실험적 구현이 가능해지며, 구현자는 명세에 따라 올바르게 구현되었음을 입증할 수 있는 CR 단계의 기능에 대해 접두사 없는 구현을 출시해야 합니다.

CSS의 구현 간 상호 운용성을 확립하고 유지하기 위해, CSS 작업 그룹은 비실험적 CSS 렌더러가 W3C에 구현 보고서(필요하다면 해당 구현 보고서에 사용된 테스트 케이스 포함)를 제출한 후에 CSS 기능의 접두사 없는 구현을 출시할 것을 요청합니다. W3C에 제출된 테스트 케이스는 CSS 작업 그룹에 의해 검토 및 수정될 수 있습니다.

테스트 케이스 및 구현 보고서 제출에 관한 추가 정보는 CSS 작업 그룹 웹사이트 https://www.w3.org/Style/CSS/Test/에서 확인할 수 있습니다. 문의는 public-css-testsuite@w3.org 메일링 리스트로 보내야 합니다.

색인

이 명세서에서 정의된 용어

참조로 정의된 용어

참고 문헌

규범적 참고 문헌

[CSS-BREAK-3]
Rossen Atanassov; Elika Etemad. CSS Fragmentation Module Level 3. 2017년 2월 9일. CR. URL: https://www.w3.org/TR/css-break-3/
[CSS-CASCADE-4]
Elika Etemad; Tab Atkins Jr.. CSS Cascading and Inheritance Level 4. 2016년 1월 14일. CR. URL: https://www.w3.org/TR/css-cascade-4/
[CSS-FLEXBOX-1]
Tab Atkins Jr.; Elika Etemad; Rossen Atanassov. CSS Flexible Box Layout Module Level 1. 2017년 10월 19일. CR. URL: https://www.w3.org/TR/css-flexbox-1/
[CSS-GRID-1]
Tab Atkins Jr.; Elika Etemad; Rossen Atanassov. CSS Grid Layout Module Level 1. 2017년 12월 14일. CR. URL: https://www.w3.org/TR/css-grid-1/
[CSS-INLINE-3]
Dave Cramer; Elika Etemad; Steve Zilles. CSS Inline Layout Module Level 3. 2016년 5월 24일. WD. URL: https://www.w3.org/TR/css-inline-3/
[CSS-OVERFLOW-3]
David Baron; Florian Rivoal. CSS Overflow Module Level 3. 2016년 5월 31일. WD. URL: https://www.w3.org/TR/css-overflow-3/
[CSS-PAGE-FLOATS-3]
Johannes Wilm. CSS Page Floats. 2015년 9월 15일. WD. URL: https://www.w3.org/TR/css-page-floats-3/
[CSS-POSITION-3]
Rossen Atanassov; Arron Eicholz. CSS Positioned Layout Module Level 3. 2016년 5월 17일. WD. URL: https://www.w3.org/TR/css-position-3/
[CSS-PSEUDO-4]
Daniel Glazman; Elika Etemad; Alan Stearns. CSS Pseudo-Elements Module Level 4. 2016년 6월 7일. WD. URL: https://www.w3.org/TR/css-pseudo-4/
[CSS-RUBY-1]
Elika Etemad; Koji Ishii. CSS Ruby Layout Module Level 1. 2014년 8월 5일. WD. URL: https://www.w3.org/TR/css-ruby-1/
[CSS-SIZING-3]
Tab Atkins Jr.; Elika Etemad. CSS Intrinsic & Extrinsic Sizing Module Level 3. 2018년 3월 4일. WD. URL: https://www.w3.org/TR/css-sizing-3/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 2014년 2월 20일. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-TYPED-OM-1]
Shane Stephens; Tab Atkins Jr.. CSS Typed OM Level 1. 2017년 8월 1일. WD. URL: https://www.w3.org/TR/css-typed-om-1/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3. 2016년 9월 29일. CR. URL: https://www.w3.org/TR/css-values-3/
[CSS-WRITING-MODES-3]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 3. 2017년 12월 7일. CR. URL: https://www.w3.org/TR/css-writing-modes-3/
[CSS21]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 2011년 6월 7일. REC. URL: https://www.w3.org/TR/CSS2/
[CSS22]
Bert Bos. Cascading Style Sheets Level 2 Revision 2 (CSS 2.2) Specification. 2016년 4월 12일. WD. URL: https://www.w3.org/TR/CSS22/
[CSS3-DISPLAY]
Elika Etemad. CSS Display Module Level 3. 2017년 7월 20일. WD. URL: https://www.w3.org/TR/css-display-3/
[CSSOM-1]
Simon Pieters; Glenn Adams. CSS Object Model (CSSOM). 2016년 3월 17일. WD. URL: https://www.w3.org/TR/cssom-1/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997년 3월. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[WebIDL]
Cameron McCormack; Boris Zbarsky; Tobie Langel. Web IDL. 2016년 12월 15일. ED. URL: https://heycam.github.io/webidl/
[WORKLETS-1]
Ian Kilpatrick. Worklets Level 1. 2016년 6월 7일. WD. URL: https://www.w3.org/TR/worklets-1/

참고용 참고 문헌

[CSS-BACKGROUNDS-3]
Bert Bos; Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. 2017년 10월 17일. CR. URL: https://www.w3.org/TR/css-backgrounds-3/
[CSS-MULTICOL-1]
Håkon Wium Lie; Florian Rivoal; Rachel Andrew. CSS Multi-column Layout Module Level 1. 2017년 10월 5일. WD. URL: https://www.w3.org/TR/css-multicol-1/

IDL 색인

[Exposed=LayoutWorklet]
interface LayoutChild {
    readonly attribute StylePropertyMapReadOnly styleMap;

    IntrinsicSizesRequest intrinsicSizes();
    LayoutFragmentRequest layoutNextFragment(LayoutConstraints constraints, ChildBreakToken breakToken);
};

[Exposed=LayoutWorklet]
interface LayoutFragment {
    readonly attribute double inlineSize;
    readonly attribute double blockSize;

    attribute double inlineOffset;
    attribute double blockOffset;

    readonly attribute any data;

    readonly attribute ChildBreakToken? breakToken;
};

[Exposed=LayoutWorklet]
interface IntrinsicSizes {
  readonly attribute double minContentSize;
  readonly attribute double maxContentSize;
};

[Constructor(optional LayoutConstraintsOptions options),Exposed=LayoutWorklet]
interface LayoutConstraints {
    readonly attribute double availableInlineSize;
    readonly attribute double availableBlockSize;

    readonly attribute double? fixedInlineSize;
    readonly attribute double? fixedBlockSize;

    readonly attribute double percentageInlineSize;
    readonly attribute double percentageBlockSize;

    readonly attribute double? blockFragmentationOffset;
    readonly attribute BlockFragmentationType blockFragmentationType;

    readonly attribute any data;
};

dictionary LayoutConstraintsOptions {
    double availableInlineSize = 0;
    double availableBlockSize = 0;

    double fixedInlineSize;
    double fixedBlockSize;

    double percentageInlineSize;
    double percentageBlockSize;

    double blockFragmentationOffset;
    BlockFragmentationType blockFragmentationType = "none";

    any data;
};

enum BlockFragmentationType { "none", "page", "column", "region" };

[Exposed=LayoutWorklet]
interface ChildBreakToken {
    readonly attribute BreakType breakType;
    readonly attribute LayoutChild child;
};

[Exposed=LayoutWorklet]
interface BreakToken {
    readonly attribute sequence<ChildBreakToken> childBreakTokens;
    readonly attribute any data;
};

dictionary BreakTokenOptions {
    sequence<ChildBreakToken> childBreakTokens;
    any data = null;
};

enum BreakType { "none", "line", "column", "page", "region" };

[Exposed=LayoutWorklet]
interface LayoutEdgeSizes {
  readonly attribute double inlineStart;
  readonly attribute double inlineEnd;

  readonly attribute double blockStart;
  readonly attribute double blockEnd;

  // Convenience attributes for the sum in one direction.
  readonly attribute double inline;
  readonly attribute double block;
};

[Exposed=LayoutWorklet]
interface LayoutEdges {
  readonly attribute LayoutEdgeSizes border;
  readonly attribute LayoutEdgeSizes scrollbar;
  readonly attribute LayoutEdgeSizes padding;

  readonly attribute LayoutEdgeSizes all;
};

partial interface CSS {
    [SameObject] readonly attribute Worklet layoutWorklet;
};

[Global=(Worklet,LayoutWorklet),Exposed=LayoutWorklet]
interface LayoutWorkletGlobalScope : WorkletGlobalScope {
    void registerLayout(DOMString name, VoidFunction layoutCtor);
};

[Exposed=LayoutWorklet]
dictionary LayoutOptions {
  ChildDisplayType childDisplay = "block";
  LayoutSizingMode sizing = "block-like";
};

[Exposed=LayoutWorklet]
enum ChildDisplayType {
    "block",
    "normal",
};

[Exposed=LayoutWorklet]
enum LayoutSizingMode {
    "block-like",
    "manual",
};

[Exposed=LayoutWorklet]
interface IntrinsicSizesRequest {
};

[Exposed=LayoutWorklet]
interface LayoutFragmentRequest {
};

typedef (IntrinsicSizesRequest or LayoutFragmentRequest)
    LayoutFragmentRequestOrIntrinsicSizesRequest;

// This is the final return value from the author defined layout() method.
dictionary FragmentResultOptions {
    double inlineSize = 0;
    double blockSize = 0;
    double autoBlockSize = 0;
    sequence<LayoutFragment> childFragments = [];
    any data = null;
    BreakTokenOptions breakToken = null;
};

dictionary IntrinsicSizesResultOptions {
    double maxContentSize;
    double minContentSize;
};

이슈 색인

이슈: 현재 API는 반복 가능한 generator 형태입니다. 구현 경험과 웹 개발자 경험에 따라, 이것이 promise 기반 API로 변경될 수도 있습니다. 각각 장단점이 존재합니다.

Promises

Generator

layoutInstance와 유사하게 styleMapbox에 저장하는 것이 더 나을 수도 있습니다.
layoutInstance와 유사하게 styleMapbox에 저장하는 것이 더 나을 수도 있습니다.