환경설정하기
패키지 관리 및 빌드도구로 volo를 사용할 겁니다.
첫번째 단계
volo를 사용하기 위해 Node.js를 설치합니다. 윈도우와 맥 사용자는 여기에서 다운받으세요. 리눅스 사용자는 패키지관리자를 사용해서 설치하세요 : 패키지관리자를 사용해서 설치하는 방법
두번째 단계
volo 설치하기
npm install -g volo |
*nix 환경에서는 명령을 사용하기 전에 sudo를 앞에 붙여야 합니다.
세번째 단계
‘volo create’ 명령을 사용해서 fos-todo-app
이라는 프로젝트를 생성합니다.
volo create fos-todo-app |
fos-todo-app
디렉토리가 생기고 GitHub에서 volo 프로젝트를 파일을 복사할 겁니다.
참고사항 앱은 fos-todo-app/www
에서만 개발할 겁니다. 여러분이 volo를 사용한 경험이 없다면 이 글(volo를 사용하는 방법 : http://net.tutsplus.com/tutorials/javascript-ajax/streamline-your-process-with-volo/ )을 읽어보길 권합니다.
네번째 단계
Firefox 최신버전을 사용하세요. 그리고나서 시뮬레이터 (https://addons.mozilla.org/en-US/firefox/addon/firefox-os-simulator/) 를 설치하세요. 이 글의 마지막에서 시뮬레이터에서 앱 UI를 테스트할 겁니다.
다섯번째 단계
MozTT 폰트 (https://github.com/mozilla-b2g/moztt/tree/master/MozTT)를 받아서 설치하세요. MozTT는 Firefox OS에서 사용할 폰트입니다. 여기는 그냥 지나가도 되지만 여러분이 Firefox OS 시뮬레이터와 실제 폰에서 일관된 화면을 얻길 원하면 이 폰트를 사용하는 것이 더 좋습니다.
Todo 화면
header에는 제목, footer에는 있는 추가/삭제 버튼이 있고 할일 목록이 있는 첫번째 UI를 빌드할 겁니다. 이 화면은 할일을 보고, 수정하고, 완료 표시를 하고, 삭제할 때 사용할 겁니다.
Todo화면을 만들기 위해 ‘body’에 role="application"
을 추가하고 다음에 나오는 HTML을 추가하세요.
<section id="view-todos" role="region"> <header> <h1>Todos</h1> </header> <article class="view-content"> <ul id="todo-list" class="todo-list reset-list" role="list"> <li role="listitem"> <label> <input type="checkbox"> <span>My task 1</span> </label> <input type="text" aria-hidden="true"> <button class="btn edit"><span>Edit</span></button> </li> <li role="listitem"> <label> <input type="checkbox" checked> <span>My task 2</span> </label> <input type="text" aria-hidden="true"> <button class="btn edit"><span>Edit</span></button> </li> </ul> </article> <footer> <menu role="toolbar"> <button role="menuitem" class="btn add"><span>Add</span></button> <button role="menuitem" class="btn del" disabled><span>Delete</span></button> </menu> </footer> </section><!--end view-todos--> |
각 화면은 role="region"
가 지정되어 있습니다. 화면(view)에 여러분은 header
, footer
와 화면의 콘텐츠를 담고 있는 view-content
클래스를 가진 컨테이너를 넣을 수 있습니다.
이전 화면의 내용에서 우리는 사용자가 입력한 할일을 표시할 ul[role=list]
를 사용했습니다. 테스트를 위해 나는 두 개의 할일을 넣었으며 여러분이 목록의 구조(`li[role=listitem]`)를 이해하기 쉽게 했습니다. 각 항목에서 우리는 <input type="checkbox">
를 가진 label
과 할일의 내용을 가진 span
을 사용했습니다. 항목에서 사용한 두번째 요소는 aria-hidden="true"
를 사용하여 숨겨진 <input type="text">
입니다. 마지막으로 세번째 요소는 .btn.edit
(수정버튼)입니다.
참고사항:물론 이것은 마술이 아닙니다. 우리는 모든 요소를 CSS로 만들었습니다. 여러분이 WAI-ARIA가 생소하다면 ARIA and Progressive Enhancement 를 참고하세요.
항목에서 label
을 사용한 목적은 할일의 내용과 내용을 수정할 때 사용되어 질 input[type=text]
(기본적으로 숨겨져 있는)를 보여주는 것입니다.
footer
에는 추가/삭제 버튼을 가진 menu[role=toolbar]
를 위치시켰습니다. 이후에 CSS를 사용하여 버튼을 만들어 우리는 배경에 svg 아이콘을 보여주고 inner span을 숨길 것입니다.
추가 화면
우리의 앱의 두번째이자 마지막 화면은 할일 목록에 할일을 추가하는 화면입니다.
이 화면에서 할일을 목록을 넣기 위한 텍스트박스와 웹 액티비티를 사용하여 연락처 번호를 추가할 연락처 링크를 위치시켰습니다.
<section id="view-add" role="region"> <header> <h1>[+] Todo</h1> </header> <section class="view-content"> <div class="wrapper"> <menu class="options"> <ul id="activities" class="reset-list" role="list"> <li role="listitem"><a href="#"><label for="task">[Insert Contact]</label></a></li> </ul> </menu> <form aria-owns="btn-add-done"> <input type="text" name="task" id="task" required> </form> </div> </section> <footer> <menu role="toolbar"> <button id="btn-add-done" role="menuitem" class="btn done" disabled><span>Done</span></button> <button role="menuitem" class="btn del"><span>Cancel</span></button> </menu> </footer> </section><!--end view-add--> |
section.view-content
안의 내용은 div.wrapper
에 둘러싸서 max-width
를 제한하고 menu.options
을 오른쪽으로 위치시켰음을 참고하세요.
두번째로 참고할 사항은 form
에 있는 aria-owns
입니다. 이 항목은 btn-add-done
과 함께 사용된 form
과 관련이 있습니다.
요소(HTML 태그) 스타일
CSS를 사용해봅시다. 각 선택자 이전에 주석을 달아서 설명해놨습니다.
/* Stretch vertically */ html, body { height: 100%; } body { background: radial-gradient(ellipse at center, rgba(231,76,60,1) 0%,rgba(192,57,43,1) 100%); color: #fff; font-family: MozTT, sans-serif; /* Reset base size */ font-size: 16px; /* Set font size relative to base size */ /* Check <a href="http://snook.ca/archives/html_and_css/font-size-with-rem">this</a> article on rem usage */ font-size: 1.4rem; font-weight: 300; /* Remove spacing */ margin: 0; } h1, h2, h3, h4, h5, h6 { font-weight: 300; } h1 { font-size: 3rem; } a { color: #FFA49E; text-decoration: none; text-shadow: 0px 1px 1px rgba(0,0,0,.3); } a:hover { color: #fff; } input, button, select, textarea { border-radius: 0; box-shadow: none; } input[type=text], textarea, select { border: none; font-family: MozTT, sans-serif; font-size: 1.3rem; font-weight: 300; padding: .7rem .8rem; } |
화면 전용 스타일
#view-todos > .view-content { margin-left: 0; margin-right: 0; } #view-add > .view-content { margin-top: 2rem; } #view-add > .view-content > .wrapper { overflow: hidden; margin: 0 auto; max-width: 800px; width: 100%; } #view-add input[name=task] { width: 100%; } #view-add .options { margin: 0 0 .5rem 0; text-align: right; } #view-add .options [role=listitem] { margin: .5rem 0; } #view-add .options a { padding: .4rem; } |
모듈 스타일
화면 모듈
/* All views have region role so selecting them with attribute selector */ [role=region] { background: radial-gradient(ellipse at center, rgba(231,76,60,1) 0%,rgba(192,57,43,1) 100%); /* Stretch both vertically/horizontally */ top: 0; right: 0; bottom: 0; left: 0; position: fixed; overflow-x: hidden; } [role=region] > header { margin: .5rem 0 1rem 0; text-align: center; } [role=region] > header > h1 { margin: 0; text-shadow: 0px 1px 1px rgba(0,0,0,.4); } [role=region] > .view-content { margin: 0 1rem 4rem 1rem; } [role=region] > footer { background: rgba(0,0,0,.2); right: 0; bottom: 0; left: 0; position: fixed; text-align: center; } [role=region] > footer > menu[role=toolbar] { margin: 0; padding: 0; } |
버튼 모듈
.btn { border: none; width: 3.5rem; height: 3.5rem; } .btn > span { display: none; } .btn:disabled { opacity: .2; } .btn.done { background: url(../img/check.svg) no-repeat center center; } .btn.add { background: url(../img/add.svg) no-repeat center center; } .btn.del { background: url(../img/del.svg) no-repeat center center; } .btn.edit { background: url(../img/edit.svg) no-repeat center center; } .btn:hover { background-color: rgba(255,255,255,.1); } |
할일 목록 모듈
.todo-list > li { border-bottom: 1px solid rgba(255,255,255,.1); position: relative; } .todo-list > li > .btn { top: 0; right: 0; position: absolute; height: 3.9rem; } .todo-list label { border-bottom: 1px solid rgba(0, 0, 0, 0.1); display: block; padding: 1rem 2.8rem 1rem 1rem; } .todo-list > li:last-child, .todo-list > li:last-child > label { border-bottom: none; } .todo-list label > span { transition: all .5s; } /*** This selector is interesting. It creates a line-through and decrease opacity of text in span when checkbox is checked ***/ .todo-list input[type=checkbox]:checked + span { text-decoration: line-through; opacity: .4; } .todo-list > li > input[type=text] { margin: .4rem; /* removing input padding and margin from 100% */ width: calc(100% - 2.3rem); } |
유틸리티 스타일
.reset-list { list-style: none; margin: 0; padding: 0; } [aria-hidden=true] { display: none !important; } |
애니메이션
화면을 동적으로 보이기 위해 몇가지 애니메이션 효과를 사용할 겁니다. JavaScript를 사용한 CSS-3 애니메이션 런타임 라이브러리인 Zepto.js 을 사용할 겁니다. 개인적으로 CSS3를 사용해서 애니메이션을 표현하고 CSS 클래스를 적용/제거함으로써 이것을 변경하는 걸 즐깁니다.
Zepto는 jQuery API와 호환되는 Javascript 라이브러리 입니다. 여러분이 jQuery를 사용하고 있다면 벌써 Zepto를 사용하는 방법을 아는 것입니다. – zeptojs.com
CSS3 애니메이션을 더 많이 이해하기 위해서 CSS 애니메이션 사용하기 (http://css3.bradshawenterprises.com/animations/) 소개를 참고하세요.
.slide-up-in { animation-name: slide-up-in; animation-duration: .6s; transform: translateY(0%); opacity: 1; } @keyframes slide-up-in { 0% { transform: translateY(30%); opacity: 0; } 100% { transform: translateY(0%); opacity: 1; } } .slide-down-out { animation-name: slide-down-out; animation-duration: .6s; transform: translateY(30%); opacity: 0; } @keyframes slide-down-out { 0% { transform: translateY(0%); opacity: 1; } 100% { transform: translateY(30%); opacity: 0; } } .slide-right-in { animation-name: slide-right-in; animation-duration: .6s; transform: translateX(0%); } @keyframes slide-right-in { 0% { transform: translateX(-100%); } 100% { transform: translateX(0%); } } .slide-left-out { animation-name: slide-left-out; animation-duration: .6s; transform: translateX(-100%); } @keyframes slide-left-out { 0% { transform: translateX(0%); } 100% { transform: translateX(-100%); } } |
Firefox OS 시뮬레이터에서 테스트하기
Firefox OS 시뮬레이터를 사용하여 여러분은 매우 쉽게 개발된 UI를 테스트할 수 있습니다. 필요한 것은 단지 루트 디렉토리(fos-todo-app/www)에 있는 manifest.webapp
입니다. 다음 json을 추가하세요.
{ "version": "0.1", "name": "Todos", "description": "Awesome todo app by iFadey", "launch_path": "/index.html", "icons": { "16": "/img/icons/icon-16.png", "48": "/img/icons/icon-48.png", "128": "/img/icons/icon-128.png" }, "developer": { "name": "Fawad Hassan", "url": "http://ifadey.com" }, "installs_allowed_from": ["*"], "appcache_path": "/cache.manifest", "default_locale": "en" } |
지금은 app icon을 추가하지 않겠지만 그 점은 염려하지 않아도 됩니다. manifest 파일안에서 찾지 못한다면 Firefox OS가 기본 아이콘을 표시해줄 것입니다. 지금 시뮬레이터(개발자도구 > Firefox OS Simulator) 를 실행시켜 봅시다. 그리고 나서 Add Directory
를 누르고 manifest 파일을 선택하세요. 시뮬레이터에 우리의 앱이 추가될 겁니다.
첫 회의 마지막
여러분은 Todo 앱의 시작점에 왔고 몇가지 단계를 지나왔습니다. 다음 회에서는 앱에 JavaScript (Backbone/Zepto)를 사용해서 생명을 불어 넣을 것입니다. Todo 모델, 콜렉션과 뷰를 생성하고 간단하게 todo 콜렉션을 IndexedDB 에 저장할 것입니다. 또한 할일 내용에 있는 연락처를 입력하기 위해 웹 액티비티를 사용할 것입니다.
작성자: LAWN
설수웅(SwooWoong, Seol) / Software & Data Engineer / I love technology to improve people's lives, and harmonious architecture with nature.(LAWN) / 인간의 삶을 향상시키는 기술, 자연과 조화로운 아키텍쳐를 사랑합니다.
댓글이 없습니다.