CSS 지역 및 제외가 포함된 잡지와 유사한 웹용 레이아웃

Christian Cantrell
Christian Cantrell

소개

웹은 텍스트를 위한 매우 강력한 플랫폼으로, Adobe에서 풍부한 경험과 전문성을 갖추고 있습니다. Adobe가 웹을 발전시키는 데 도움을 줄 방법을 모색할 때, 웹의 텍스트 기능을 더욱 발전시키는 것이 저희에게 출발점이 될 것 같았습니다. 웹에서는 일반적으로 텍스트가 세로 방향인 단일 열이라고 가정합니다. 텍스트를 그래픽 주변에 배치하고 CSS를 사용하여 텍스트를 여러 열로 서식을 지정할 수도 있지만 웹에서 진정한 잡지와 같은 레이아웃을 구현하기는 매우 어렵습니다. Adobe는 CSS 지역CSS 제외를 통해 최신 브라우저에 데스크톱 게시 기능을 제공하기 위한 노력을 이끌고 있습니다. 예를 들어 아래 스크린샷에서는 CSS 제외가 산의 윤곽을 따라 텍스트를 이동하는 데 사용되고 있습니다.

CSS 제외 적용 예시
CSS 제외 적용 예시

아래 스크린샷의 문서에서는 CSS 제외를 사용하여 이미지에 포함된 도형을 텍스트로 둘러싸고, CSS 영역을 사용하여 텍스트의 서식을 열 및 인용문 주위로 지정합니다.

실제 CSS 영역의 예
CSS 리전의 예시

CSS 영역

CSS 지역에 대해 자세히 알아보기 전에 Chrome에서 지역을 사용 설정하는 방법을 설명하겠습니다. CSS 지역을 사용 설정한 후 이 도움말에서 참조한 샘플 중 일부를 사용해 보고 직접 샘플을 만들 수 있습니다.

Chrome에서 CSS 영역 사용 설정

Chrome 버전 20 (정확히 버전 20.0.1132.57)부터 chrome://flags 인터페이스를 통해 CSS 지역이 사용 설정됩니다. CSS 지역을 사용 설정하려면 다음 단계를 따르세요.

  1. Chrome에서 새 탭 또는 창을 엽니다.
  2. 주소 표시줄에 chrome://flags을 입력합니다.
  3. 페이지에서 찾기 (Ctrl/Command + f)를 사용하여 '실험용 웹 플랫폼 기능' 섹션을 검색합니다.
  4. 사용 설정 링크를 클릭합니다.
  5. 하단의 지금 다시 시작 버튼을 클릭합니다.

Chrome 신고에 대한 자세한 내용은 Chrome 신고에 대한 모든 정보에 관한 블로그 게시물을 참고하세요.

브라우저를 다시 실행한 후에도 CSS 지역을 실험할 수 있습니다.

CSS 영역 개요

CSS 지역을 사용하면 의미론적으로 마크업된 텍스트 블록을 '상자' (현재 요소)에 자동으로 배치할 수 있습니다. 아래 그림은 텍스트 (흐름)와 상자 (텍스트가 들어가는 영역)의 구분을 보여줍니다.

콘텐츠가 정의된 영역으로 이동
콘텐츠가 정의된 지역으로 이동

실제 CSS 지역의 사용 사례를 살펴보겠습니다. 저는 Adobe의 개발자일 뿐만 아니라 공상과학 소설 작가이기도 합니다. 크리에이티브 커먼즈 라이선스에 따라 내 작품을 온라인에 자주 게시하며, 최대한 많은 수의 기기와 브라우저에서 작업할 수 있도록 다음과 같은 매우 단순한 형식을 자주 사용합니다.

스타일이 지정되지 않은 휴먼 레거시 프로젝트 예
스타일이 지정되지 않은 레거시 인간 프로젝트 예

CSS 지역을 사용하여, 탐색이 더 쉽고 가독성이 더 편안해졌기 때문에 시각적으로 더 흥미롭고 기능적이면서도 훨씬 더 많은 환경을 만들 수 있었습니다.

지역을 보여주는 인간의 레거시 프로젝트
리전이 있는 인간의 기존 프로젝트.

시연을 위해 이 프로토타입에 CSS 지역을 표시하는 기능을 추가했습니다. 아래 스크린샷은 그래픽을 둘러싼 열과 중앙의 인용문을 둘러싼 것처럼 보이도록 영역이 정렬되는 방식을 보여줍니다.

지역을 보여주는 인간의 레거시 프로젝트
리전을 보여주는 인간의 기존 프로젝트

여기에서 이 프로토타입으로 실험하고 소스 코드를 확인할 수 있습니다. 화살표 키를 사용하여 탐색하고 Esc 키를 눌러 영역을 표시합니다. 이전 프로토타입도 여기에서 확인할 수 있습니다.

이름이 지정된 흐름 만들기

텍스트 블록을 여러 영역으로 이동하는 데 필요한 CSS는 매우 간단합니다. 아래의 스니펫에서는 ID가 'content'인 div에 'article'이라는 이름이 지정된 흐름을 할당하고 동일한 'article'이라는 흐름을 'region' 클래스가 있는 요소에 할당합니다. 결과적으로 'content' 요소 내부에 포함된 텍스트는 'region' 클래스가 있는 모든 요소를 자동으로 통과합니다.

<!DOCTYPE html>
<html>
<head>
    <style>
    #content {
        { % mixin flow-into: article; % }
    }

    .region {
        { % mixin flow-from: article; % }
        box-sizing: border-box;
        position: absolute;
        width: 200px;
        height: 200px;
        padding: 10px;
    }

    #box-a {
        border: 1px solid red;
        top: 10px;
        left: 10px;
    }

    #box-b {
        border: 1px solid green;
        top: 210px;
        left: 210px;
    }

    #box-c {
        border: 1px solid blue;
        top: 410px;
        left: 410px;
    }
    </style>
</head>
<body>
    <div id="box-a" class="region"></div>
    <div id="box-b" class="region"></div>
    <div id="box-c" class="region"></div>
    <div id="content">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eleifend dapibus felis, a consectetur nisl aliquam at. Aliquam quam augue, molestie a scelerisque nec, accumsan non metus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin cursus euismod nisi, a egestas sem rhoncus eget. Mauris non tortor arcu. Pellentesque in odio at leo volutpat consequat....
    </div>
</body>
</html>

결과는 다음과 같습니다.

위 코드의 결과
위 코드의 결과

'content' div 내부의 텍스트는 프레젠테이션에 관한 정보가 없습니다. 즉, 다양한 지역을 흐르는 경우에도 의미론적으로는 온전히 그대로 유지될 수 있습니다. 또한 영역은 요소일 뿐이므로 다른 요소와 마찬가지로 CSS를 사용하여 위치와 크기가 지정되므로 반응형 디자인 원칙과 완벽하게 호환됩니다. 명명된 흐름의 일부로 요소를 지정하는 것은 지정된 텍스트가 요소를 통해 자동으로 이동한다는 의미입니다.

CSS 개체 모델

CSS 개체 모델(CSSOM)은 CSS 작업을 위한 JavaScript API를 정의합니다. 다음은 CSS 지역과 관련된 새 API 목록입니다.

  • document.webkitGetNamedFlows(): 문서에서 사용할 수 있는 이름이 지정된 흐름 컬렉션을 반환하는 함수입니다.
  • document.webkitGetNamedFlows().namedItem("article"): 이름이 지정된 특정 흐름의 참조를 반환하는 함수입니다. 인수는 flow-intofrom-from CSS 속성의 값으로 지정된 이름에 해당합니다. 위의 코드 스니펫에서 지정한 이름이 지정된 흐름에 대한 참조를 가져오려면 문자열 'article'을 전달합니다.
  • WebKitNamedFlow: 다음 속성 및 함수가 있는 이름이 지정된 floe의 객체 표현입니다.
    • firstEmptyRegionIndex: 이름이 지정된 흐름과 연결된 첫 번째 빈 영역의 색인을 가리키는 정수 값입니다. 지역 컬렉션을 가져오는 방법은 아래 getRegions()를 참고하세요.
    • name: 흐름 이름이 포함된 문자열 값입니다.
    • overset: 다음과 같은 불리언 속성입니다.
      • 이름이 지정된 흐름의 콘텐츠가 연결된 리전에 맞는 경우 false
      • true: 콘텐츠가 맞지 않고 모든 콘텐츠를 포함하는 데 더 많은 영역이 필요한 경우
    • getContent(): 이름이 지정된 흐름으로 흐르는 노드에 대한 참조와 함께 컬렉션을 반환하는 함수입니다.
    • getRegions(): 이름이 지정된 흐름의 콘텐츠가 있는 리전에 대한 참조가 있는 컬렉션을 반환하는 함수입니다.
    • getRegionsByContentNode(node): 지정된 노드가 포함된 리전에 대한 참조를 반환하는 함수입니다. 이는 이름이 지정된 앵커와 같은 항목이 포함된 지역을 찾는 데 특히 유용합니다.
  • webkitregionoversetchange 이벤트를 수신합니다. 이 이벤트는 연결된 콘텐츠의 레이아웃이 어떤 이유로든 변경 (콘텐츠 추가 또는 삭제, 글꼴 크기 변경, 영역의 모양 변경 등)될 때마다 WebkitNamedFlow에서 트리거됩니다. 그리고 지역의 webkitRegionOverset 속성이 변경될 수도 있습니다. 이 이벤트는 대략적인 레이아웃 변경을 수신 대기하는 데 유용합니다. 이는 중요한 일이 발생했고 레이아웃에 주의가 필요할 수 있음을 나타내는 지표입니다. 예를 들어 더 많은 영역이 필요하거나 일부 영역이 비어 있을 수 있습니다.
  • webkitregionfragmentchange 이벤트를 수신합니다. 수정 시점에 구현되지 않았습니다. 이 이벤트는 연결된 콘텐츠의 레이아웃이 webkitregionoversetchange와 유사하게 어떤 이유로든 변경될 때마다 WebkitNamedFlow에서 트리거됩니다. 하지만 webkitRegionOverset 속성의 변경사항과 관계없이 이 이벤트가 트리거됩니다. 이 이벤트는 이름이 지정된 흐름의 전체 레이아웃에 반드시 영향을 주지 않는 세분화된 레이아웃 변경을 수신 대기하는 데 유용합니다. 예를 들어 콘텐츠는 한 리전에서 다른 영역으로 이동하지만 전체 콘텐츠는 여전히 모든 리전에 적합합니다.
  • Element.webkitRegionOverset: flow-from CSS 속성이 할당되면 요소는 영역이 됩니다. 이러한 요소에는 이름이 지정된 흐름의 일부인 경우 흐름의 콘텐츠가 해당 지역을 오버플로하는지 여부를 나타내는 webkitRegionOverset 속성이 있습니다. webkitRegionOverset 값은 다음과 같습니다.
    • 지역에서 보유할 수 있는 콘텐츠보다 많은 콘텐츠가 있는 경우 'overflow'
    • 영역이 끝나기 전에 콘텐츠가 중지되는 경우 'fit'
    • 콘텐츠가 영역에 도달하지 않은 경우 'empty'

CSSOM의 주요 용도 중 하나는 webkitregionoversetchange 이벤트를 리슨하고 다양한 양의 텍스트를 수용하기 위해 리전을 동적으로 추가하거나 삭제하는 것입니다. 예를 들어 서식을 지정할 텍스트의 양을 예측할 수 없거나 (예: 사용자 생성) 브라우저 창의 크기가 조절되거나 글꼴 크기가 변경되는 경우 흐름의 변경사항을 수용하기 위해 영역을 추가하거나 삭제해야 할 수 있습니다. 또한 콘텐츠를 페이지별로 구성하려면 지역뿐만 아니라 DOM을 동적으로 수정하기 위한 메커니즘이 필요합니다.

다음 JavaScript 코드 스니펫은 필요에 따라 지역을 동적으로 추가하기 위해 CSSOM을 사용하는 방법을 보여줍니다. 여기서는 편의상 리전을 삭제하거나 리전의 크기와 위치를 정의하는 작업을 처리하지 않으며, 데모 목적으로만 사용합니다.

var flow = document.webkitGetNamedFlows().namedItem("article")
flow.addEventListener("webkitregionoversetchange", onLayoutUpdate);

function onLayoutUpdate(event) {
    var flow = event.target;
    
    // The content does not fit
    if (flow.overset === true) {
    addRegion();
    } else {
    regionLayoutComplete();
    }
}

function addRegion() {
    var region = document.createElement("div");
    region.style = "flow-from: article";
    document.body.appendChild(region);
}

function regionLayoutComplete() {
    // Finish up your layout.
}

더 많은 데모는 CSS 지역 샘플 페이지에서 확인할 수 있습니다.

CSS 페이지 템플릿

CSSOM을 활용하는 것이 페이징 및 반응형 레이아웃과 같은 기능을 구현하는 가장 강력하고 유연한 방법일 것입니다. 하지만 Adobe는 디자이너와 개발자가 상대적으로 일반적인 페이징 기능을 얻기 위한 더 쉬운 방법을 원한다는 사실을 알 수 있을 만큼 오랫동안 텍스트 및 데스크톱 게시 도구를 사용해 왔습니다. 따라서 Google은 페이징 동작을 완전히 선언적으로 정의할 수 있는 CSS Page Templates이라는 제안서를 준비하고 있습니다.

CSS 페이지 템플릿의 일반적인 사용 사례를 살펴보겠습니다. 아래의 코드 스니펫은 CSS를 사용하여 'article-flow'와 'timeline-flow'라는 두 개의 이름이 지정된 흐름을 만드는 방법을 보여줍니다. 또한 두 흐름이 포함될 'combined-articles'라는 세 번째 선택기를 정의합니다. 'combined-articles' 선택기에 overflow-style 속성을 간단히 포함하는 것만으로도 콘텐츠가 자동으로 x축을 따라 또는 가로로 페이징되어야 함을 나타냅니다.

<style>
    #article {
    { % mixin flow-into: article-flow; % }
    }

    #timeline {
    { % mixin flow-into: timeline-flow; % }
    }

    #combined-articles {
    overflow-style: paged-x;
    }
</style>

이제 흐름이 정의되고 원하는 오버플로 동작이 지정되었으므로 페이지 템플릿 자체를 만들 수 있습니다.

@template {
    @slot left {
    width: 35%;
    float: left;
    { % mixin flow-from: article-flow; % }
    }

    @slot time {
    width: 25%;
    float: left;
    { % mixin flow-from: timeline-flow; % }
    }

    @slot right {
    width: 35%;
    float: left;
    { % mixin flow-from: article-flow; % }
    }
}

페이지 템플릿은 새로운 'at' 구문을 사용하여 정의됩니다. 위의 코드 스니펫에서는 각 열에 해당하는 3개의 슬롯을 정의합니다. '기사 흐름'의 텍스트가 왼쪽과 오른쪽 열을 통해 이동하고 '타임라인-흐름'의 텍스트가 가운데 열에 채워집니다. 다음과 같은 결과가 표시될 수 있습니다.

페이지 템플릿 예
페이지 템플릿 예

기사 텍스트(왼쪽 및 오른쪽 열의 텍스트)는 영어이고 중앙의 타임라인은 독일어입니다. 또한 JavaScript 코드가 없어도 문서 페이지를 가로로 표시할 수 있습니다. 모든 작업은 CSS에서 전적으로 선언적으로 수행되었습니다.

CSS 페이지 템플릿은 여전히 제안서이지만, 자바스크립트 'shim' (일명 polyfill)을 사용하는 프로토타입이 있어서 지금 바로 실험해 볼 수 있습니다.

CSS 지역에 대한 일반적인 내용을 자세히 알아보려면 html.adobe.com의 CSS 지역 페이지를 확인하세요.

CSS 제외

잡지와 같은 레이아웃을 얻기 위해서는 텍스트가 여러 영역으로 이동하도록 하는 것만으로는 충분하지 않습니다. 시각적으로 흥미로운 고품질의 데스크톱 게시에서 중요한 요소는 불규칙한 그래픽과 모양 주위 또는 내부에서 텍스트가 흐르도록 하는 기능입니다. CSS 제외를 사용하면 웹에서 이러한 수준의 제작 품질을 누릴 수 있습니다.

아래 스크린샷은 CSS 제외 프로토타입에서 가져온 것으로, 큰 암석의 윤곽과 일치하는 경로 주위로 동적으로 흐르는 텍스트를 보여줍니다.

CSS 제외 적용 예시
CSS 제외 적용 예시

그 반대의 경우는 다음 스크린샷에서 확인할 수 있습니다. 불규칙한 모양의 다각형 내부로 흐르는 텍스트입니다.

불규칙한 모양의 다각형으로 흘러 들어가는 텍스트
불규칙한 모양의 다각형으로 전달되는 텍스트

임의의 도형 주위 또는 내부에 텍스트를 배치할 수 있는 첫 번째 단계는 필요한 알고리즘을 개발하고 최적화하는 것입니다. Adobe는 현재 WebKit에 직접 기여할 구현을 개발 중입니다. 이러한 알고리즘이 최적화되면 그 토대가 되어 CSS 제외의 나머지 부분이 구축됩니다.

CSS 제외에 대한 자세한 내용은 html.adobe.com의 CSS 제외 페이지를 참조하고 CSS 제외를 위한 기본 기술에 대한 Adobe의 작업에 대한 자세한 내용은 Hans Muller의 가로 상자: CSS 제외를 위한 다각형 교차로라는 제목의 블로그 게시물을 참고하세요.

CSS 지역 및 CSS 제외 현황

CSS 지역 및 CSS 제외에 대해 공개적으로 논의한 것은 2011년 Google I/O의 Adobe Developer Pod에서였습니다. 당시에는 자체 커스텀 프로토타입 브라우저에서 데모를 보여주고 있었습니다. 반응은 매우 열광적이었지만 제가 보여드린 주요 브라우저에서 사용할 수 있는 기능이 아직 하나도 없다는 사실을 구경자들이 발견했을 때는 실망스러웠습니다.

저는 올해 (2012년) Google I/O에 다시 참석했는데, 이번에는 제 동료 Vincent Hardy와 Google의 Alex Danilo와 함께 발표자로 참여했습니다 (여기에서 프레젠테이션 보기). 불과 1년 후 CSS 지역 사양의 약 80% 가 WebKit에 구현되었으며 이미 최신 버전의 Chrome으로 구현되었습니다 (CSS 지역은 현재 chrome://flags를 통해 사용 설정해야 함). Android용 Chrome에서도 CSS 지역에 대한 예비 지원이 제공됩니다.

Android용 Chrome 리전
Android용 Chrome의 리전

또한 CSS 지역과 CSS 제외는 모두 Internet Explorer 10 미리보기에서 구현되며, 현재 Mozilla의 2012년 Firefox용 로드맵에 포함되어 있습니다. Safari의 다음 메이저 버전은 대부분의 CSS 지역 사양을 지원해야 하며, 후속 업데이트에는 나머지 사양이 포함되어야 합니다.

다음은 2011년 4월 W3C에 대한 Google의 최초 제안 이후 CSS 지역 및 CSS 제외와 관련하여 진행된 세부적인 타임라인입니다.

지역 및 제외 진행 상황
리전 및 제외 진행 상황

결론

Adobe는 텍스트, 글꼴은 물론 InDesign과 같은 도구를 통해 데스크톱 게시와 관련하여 전반적으로 풍부한 경험을 보유하고 있습니다. 웹은 이미 텍스트를 위한 매우 강력한 플랫폼이지만, 우리는 자신의 지식과 경험을 사용하여 텍스트 프레젠테이션을 훨씬 더 폭넓게 활용하고자 합니다. CSS 지역과 CSS 제외를 통해 콘텐츠의 의미 체계를 유지하면서 실제 잡지와 같은 레이아웃을 지원하여 궁극적으로 웹을 더욱 풍부하게 표현할 수 있습니다.