3 Web Designs in 3 Weeks

第二章:Twitters' New Face

今天我们将一起完成另外一个设计,Twitter的新界面。和上一周的设计一样,它来源于dribble.com

twitter new face

这个设计是一个非常典型的Web2.0应用模式,从内容上分析,它包括下面这样一些信息:

  1. 用户本身的信息
  2. 用户发布过的一些信息
  3. 用户关注的其他用户发布的信息
  4. 热门信息
  5. 系统推荐的一些其他用户

这种模式在很多的设计中都可以用到,我们在实现的时候,顺便可以关注一下设计师是如何组织这些信息的。

第一天

在进入实际的开发之前,我们先来学习一些基本的知识,这些知识会帮助我们更好的实现页面,应用样式。

HTML5与CSS3

HTML5是下一代(新一代)HTML的标准。相较于HTML4,HTML5有了很多的修改,引入了一些新的标签,废弃了一些旧的标签,更新了一些标签的寓意等。它更关注HTML文档的语义化,使得HTML不仅仅可以被浏览器识别,更可能被爬虫或者其他应用使用等。

CSS3同样是对CSS2的扩展。它引入了众多的特性,比如自定义字体集,阴影,圆角,动画等等。在引入这些特性之前,Web开发者经常需要自己做一些绕过工作,比如实现阴影的时候会使用一张背景图片来实现,而圆角则需要为4个角都做一张小图片来拼凑起来。

HTML5的新标签

我们在这一小节主要讨论这样几个新的标签

  1. NAV
  2. HEADER
  3. ASIDE
  4. SECTION
  5. ARTICLE
  6. FOOTER

从标签的名称也可以看到HTML5在文档的语义化方面的改善。这些标签的引入,使得HTML文档更像一个文档。下面我们来分别介绍这6种标签。

nav标签的语义为:当前文档的导航。nav作为一个容器,包含了链接到其他文档或者当前文档的其他部分的链接。比如他可以被实现为当前站点的菜单栏的容器:

<nav>
    <ul>
        <li><a href="/product">Product</a></li>
        <li><a href="/contact">Contact</a></li>
        <li><a href="/detail">Detail</a></li>
    </ul>
</nav>

另外一个可能的场景是,在页脚上,通常会放置一些子站点的链接,这些链接也可以放置在nav中。比如REA是澳洲房产搜索的一个平台,它旗下有一系列的站点,比如商业地产,投资等,除了澳洲之外,它在意大利,香港等地也有站点。

rea footer

HEADER标签

header标签的语义为:当前文档的头信息或者当前文档中section的头信息。比如上周中我们的页面上的Hero Section中的两个标题:

<header>
    <h1>Relax, Finding An Agent Just Got Easier</h1>
    <h2>Receive proposals from best agents for free</h2>
</header>

或者在section中也可以使用:

<section>
    <header>
        <h1>This is my title</h1>
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eligendi officia minima iure sed enim itaque ipsa ea vel aliquam labore neque beatae qui, illo repudiandae, a laudantium voluptates reprehenderit eos?</p>
    </header>
</section>
<section>
    <header>
        <h1>This is another</h1>
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Molestiae voluptatem nam aperiam. Voluptatem animi consequatur consectetur error in fuga sit voluptatibus rerum, nisi, officiis dicta vero repudiandae ea, fugit illo.</p>
    </header>
</section>

虽然它是header,但是并不一定在物理上位于文档的头部,它更多的是一个逻辑概念。

ASIDE标签

aside的语义为:对于那些与当前文档内容相关,但又不是主要内容的内容,可以放置在aside中。通常我们会将这种内容放置在侧栏中,但是应该注意的是,侧栏并不和aside完全对应。侧栏是一个视觉设计,而aside是一个逻辑概念。

aside可以是相对于整个文档的,也可以是相对于某个seciton/article的:

<article>
    <h1>Dont design, redisign</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
    <aside>
        <h1>Colour theory</h1>
        <dl>
            <dt></dt>
            <dt></dt>
        </dl>
    </aside>
</article>

article中的aside,与article的相关性更高一些(相对于整个文档)。如果aside不在article中,那么它又应该是当前页面的次要内容。

<body>
    <header>
        <h1>Dont design, redisign</h1>
        <h2>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</h2>
    </header>
    <aside>
        <h1>Web design</h1>
        <dl>
            <dt></dt>
            <dt></dt>
        </dl>
    </aside>
</body>
SECTION标签

section可以表示一个文档,或者一组逻辑上相关的内容。但是它并不是一个通用的容器,如果仅仅是为了样式而需要引入一个容器,那么推荐使用div,而section更多的是和内容本身相关(而不是展现)。

reveal.js是一个很好的例子,它是一个用HTML编写幻灯片的工具,每张幻灯片都是相对独立的,它被包含在一个section中:

<section>
    <h2>UI for developers</h2>
    <pre><code data-item>
        ***&lt;没钱赚商店&gt;购物清单***
        名称:雪碧,数量:2瓶,单价:3.00(元),小计:6.00(元)
        名称:可口可乐,数量:4瓶,单价:15.00(元),小计:60.00(元)
        ----------------------
        挥泪赠送商品:
        名称:雪碧,数量:1瓶
        ----------------------
        总计:60.00(元)
        节省:3.00(元)
        **********************
    </code></pre>
</section>

reveal.js

ARTICLE标签

article的语义为:它表示页面上的一块独立内容,本身应该是字包含的(一个足以表意的实体)。并不局限在一篇文章这个狭义的article上。比如一篇博客,一条评论,一篇文章/新闻,一条微博等。这个独立的内容可以被分发到别处,或者被作为RSS的条目来使用。

我们今天要实现的每一天twist都可以使一个article

twist

<article class="post">
    <img src="images/juntao.png" alt="" class="avator">
    <header>
        <span class="name">Juntao</span>
        <span class="account">@juntao</span>
        <span class="time">2m</span>
    </header>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloribus veniam quas, deserunt! Provident sit, impedit, facilis molestias minus quas hic ad deserunt quia, alias velit architecto debitis itaque doloremque quaerat.</p>
    <footer>
    </footer>
</article>
FOOTER标签

footerheader可以对照着看,它也不仅仅是狭义的页脚,还可以被使用在section或者article中。在理解这些元素的时候,尽量放在文档/内容的角度来考虑,而不是从视觉上考虑。比如footer并不一定非要放在页面的最底部。

<footer>
    <nav>
        <a href="/product">Product</a>
        <a href="/contact">Contact</a>
        <a href="/detail">Detail</a>
    </nav>
    <h2>CopyRight@2014</h2>
</footer>

CSS3新特性

CSS3引入了众多的新特性,使得Web设计变得非常容易。在此之前,人们需要借助JavaScript技巧,精心设计的图片等来完成一些设计上的效果。目前CSS3的众多特性已经为所有的主流浏览器所支持,在你的设计中,你也可以放心的使用这些特性了。当然,异常总是存在的,比如奇葩的IE系列(事实上,我本人总是有意无意的在躲避IE)。

在介绍其他例子之前,我们先定义一个基准,假设我们有一个很简单的Box:

<section class="container">
    <section class="box">
        I'm a box
    </section>
</section>

baseline

圆角

圆角风格可以让一个带有边框的元素看起来没有那么锋利,事实上苹果公司的很多设计都带有一些圆角,比如窗口,桌面图标等。

在CSS3中,实现圆角非常简单,只需要定义一个圆角的半径即可:

.rounded {
    border-radius: 8px 8px 8px 8px;
}

这四个数字分别表示:左上,右上,右下,左下。当然,如果四个角的圆角半径一样的话,可以简写为:

.rounded {
    border-radius: 8px;
}

rounded

背景渐变

背景渐变可以以渐变的方式来修改一个元素的背景色,从而展现出立体的效果。背景渐变可以分为两类:线性渐变径向渐变。线性渐变需要指定一个方向,然后一组颜色。

颜色值按照方向上的长度被分配,比如下面这个例子中我们使用了两个颜色:

.gradient {
    background: linear-gradient(to bottom, #eee, #666);
}

gradient two colors

如果使用三种颜色:

.gradient {
    background: linear-gradient(to bottom, #eee, #666, #eee);
}

gradient

径向渐变默认的会从圆心开始,并逐渐按照径向来分配选中的一组颜色:

.gradient-rounded {
    background: radial-gradient(#666, #eee, #666);
}

gradient rounded

盒阴影

盒阴影可以体现元素的立体感,在之前,要实现一个阴影,开发者需要绘制两个不同颜色的元素,然后叠加在一起。CSS3中的盒阴影可以大大简化这个过程。

定义一个盒阴影,需要指定这样一些参数:阴影在x轴上的偏移,阴影在y轴上的偏移,阴影的模糊半径,阴影的颜色。

.box-shadow {
    box-shadow: 5px 5px 0 orange;
}

box shadow

上例中我们将模糊半径设置为了0,从而得到了一个清晰的阴影,如果将模糊半径设置为5px

box shadow blur

如果将偏移设置为负数,则可以得到一个在左上角的偏移:

.box-shadow {
    box-shadow: -5px -5px 5px orange;
}

box shadow minus

盒阴影还有一个参数,可以实现内阴影:

.box-shadow {
    box-shadow: -5px -5px 5px orange inset;
}

box shadow inset

文字阴影

和盒阴影相似,文字阴影用来为文字添加立体的效果。

.text-shadow {
    text-shadow: 2px 2px 1px gray;
}

text shadow

过渡(transition)

过渡发生在元素在两个状态中切换时,它会缓慢的的将元素从一个状态变到另外一个状态。

比如我们上例中的box,假设其样式为:

.box {
    background: white;
    color: orange;
}

hover时,其样式变成了

.box:hover {
    background: orange;
    color: white;
}

这时候从鼠标进入box到前景色/背景色互换,几乎没有延迟,这在某些情况下会变得比较烦人,特别是当鼠标在一个列表中通过时,会给人以眼花缭乱的感觉。过渡则可以让这个过程变得舒缓:

.box {
    background: white;
    color: orange;
    transition: all .5s ease-in-out;
}

这时候当鼠标进入box到实际颜色发生变化需要0.5秒,而ease-in-out是一个函数名,相应的还有ease-inease-out等。他们的区别在时间很短的时候几乎觉察不到,大家可以在这里看到详细的解释。

变换

变换事实上为我们提供了基本的动画能力,比如我们在很多网站上都见到过这样的效果:当鼠标挪到一张图片的缩略图上时,图片会变大一些,挪开之后又会复原。当然,使用JavaScript也可以模拟这个动作,不过CSS3中已经自带了这样的特性。

在CSS3中使用变换非常简单,比如刚才提到的这个效果:

.element:hover {
    transform: scale(1.2);
}

当鼠标挪进该元素时,元素会变为之前的1.2倍大。另一个常见的变换效果是旋转:

.element:hover {
    transform: rotate(180deg);
}

当鼠标挪进该元素时,元素会旋转180度。我们最后再来看一个移动的变换:

.element:hover {
    transform: translate(200px, 100px);
}

当鼠标挪进该元素时,元素会向右下方移动(x轴方向200像素,y轴方向100像素)。CSS3事实上支持非常多的变换函数,这里就不一一列举了,读者可以参考这个详细的函数列表

Marking up

好了,我们已经学习了足够多的理论知识了,下面就开始实际的页面开发吧,相信你已经摩拳擦掌,跃跃欲试了吧?

我们先来尝试编写整个页面的HTML结构,我们可以使用之前学习的HTML5的新标签,但是也不用彻底丢弃HTML4的标签:

<header>
    <nav>
        <ul>
            <li><a href="#" class="compose"></a></li>
            <li><a href="#" class="alert"></a></li>
            <li><a href="#" class="email"></a></li>
            <li><a href="#" class="tag"></a></li>
        </ul>
    </nav>
</header>
<aside>
    <div class="profile"></div>
    <div class="gallery"></div>
    <div class="activities"></div>
</aside>
<section class="content">
    <nav class="info">
        <ul>
            <li class="number">200</li>
            <li class="number">120</li>
            <li class="number">180</li>
            <li class="number">200</li>
        </ul>
    </nav>
    <div class="articles">
        <article></article>
        <article></article>
        <article></article>
    </div>
</section>
<aside>
    <div class="trends"></div>
    <div class="to-follow"></div>
</aside>
<footer>
    <div class="copyright"></div>
</footer>

我们将页面分为5个主要的区域:头部,左边栏,内容区,右边栏,页脚。当然这只是一个初步的划分,如果我们发现有需要重新调整的地方,随时可以回来进行修改。

第二天

有了大的划分之后,我们就可以进行实际的开发了。我们先选择页面上的Tweet条目进行实现,这部分非常有代表性,很多其他Web设计都包含了这样的模式,因此学会这个可以帮助我们快速的实现页面的其他部分。

Tweet条目的实现

一个Tweet在设计上是这样的:

tweet

对于每一条Tweet,根据HTML5对article的描述,我们其实可以很自然的将Tweet实现为article,所以我们可以这样定义一个Tweet:

<article class="post">
    <img src="images/juntao.png" alt="" class="avator">
    <section class="content">
        <header>
            <span class="name">Juntao</span>
            <span class="account">@juntao</span>
            <span class="time">2m</span>
        </header>
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloribus veniam quas, deserunt! Provident sit, impedit, facilis molestias minus quas hic ad deserunt quia, alias velit architecto debitis itaque doloremque quaerat.</p>
        <footer>
            <nav>
                <a href=""><i class="icon-redo"></i></a>
                <a href=""><i class="icon-star"></i></a>
                <a href=""><i class="icon-loop"></i></a>
                <a href=""><i class="icon-ellipsis"></i></a>
                <a href=""><i class="icon-maximize"></i></a>
            </nav>
        </footer>
    </section>
</article>

每个Tweet中,首先需要一张作者的头像,然后是用户昵称,用户名,时间,正文,在正文下面有一些link,包括转发,赞等。这种布局方式非常常见,你可以仔细看一下上一周的设计,同样会发现这种模式:左边是一个独立的图片,右边是另外一大块的信息,右边的信息都严格的左对齐。

通常要实现这种布局,我们需要先为左右两部分设置一个相同的父元素,比如此处的post。然后将父元素的position设置为relative,将左边的元素的position设置为absolute,这样左边的元素就可以相对于父元素绝对定位了。最后,为右边的元素设置一个margin-left,空出一定的距离给左边的元素。

在未应用样式之前:

tweet origin

我们先来为头像加上样式,首先需要限定头像的大小(30x30),然后将border-raduis设置为100%,这样可以得到一个完美的圆。最后,将其对于父元素的偏移设置好:

.avator {
    width: 3em;
    height: 3em;
    @include border-radius(100%);
    position: absolute;
    top: 2em;
    left: 2em;
}

tweet avatar

然后我们需要将用户昵称,用户名等浮动起来,并且将底部的那些可以点击的链接也浮动起来:

.post {
    position: relative;
    text-align: left;
    padding: 2em;
    background: $post-bg-color;
    border-bottom: 1px solid $body-color;

    .content {
        margin-left: 4em;

        span {
            margin-right: 1em;

            &.name {
                font-weight: bold;
            }

            &.account {
                font-weight: lighter;
                color: $gray-text-color;
            }

            &.time {
                color: $gray-text-color;
                float: right;
                margin-right: 0;
            }
        }
    }

    header {
        margin-bottom: 1em;
    }

    section {
        margin-bottom: 1em;
        p {
            line-height: 1.4;
            font-weight: normal;
        }
        img {
            width: 100%;
            margin-bottom: 1em;
        }
    }

    footer {
        a {
            text-decoration: none;
            color: $gray-text-color;
            margin-right: 1em;
            &:last-child {
                float: right;
                margin-right: 0;
            }

            &:hover {
                color: $text-color;
            }
        }
    }
}

这样就可以得到一个非常接近设计稿的Tweet了:

tweet final

CSS3颜色

在CSS2中,颜色可以通过三种方式来表示

  1. 预定义颜色名,如redorange
  2. 十六进制,如#004c97
  3. RGB表示法,如rgb(255, 122, 0)

颜色值有红,绿,蓝混合表示,每种颜色的取值都在区间0-255内,比如#004c97表示,红色值为0(0x00),绿色值为76(0x4c),蓝色值为151(0x97)

在CSS3中又引入了三种新的颜色表示法,分别为rgba,hsl和hsla。rgba为rgb添加了alpha通道,即透明通道。

比如:

background-color: rgba(200, 120, 80, 0.2);

最后一个数字的取值范围在区间0-1之间,如果该值为0,则表示完全透明,值为1则表示完全不透明。

CSS3引入的HSL色彩空间是一个非常有意思的表示法。我们通常描述颜色的时候,不会直接转换成RGB的值,更多的时候我们会说:我想要这个按钮是亮一点的蓝色,或者当我们看到一种不太红的红色,我们直观的想法是让它更偏向红色一些。但是这些描述无法通过RGB表示法表达出来,而HSL则可以做到这一点。

HSL分别表示Hue(色相),Saturation(饱和度),Lightness(亮度)。色相就是我们常说的红色,蓝色等;饱和度是指,在色相中参入的白色的程度,饱和度越高,色彩就越锐利,越接近色彩本身的值;亮度是在色相中参入黑色的程度,亮度越高越亮,越低越暗。我们可以通过两张图来描述清楚HSL色彩空间:

HSL

从上图可以看出HSL色彩的分布情况。使用HSL来表示颜色需要三个参数:

background-color: hsl(30, 100%, 50%);

上面的颜色定义指:色相为30,饱和度为100%,亮度为50%的颜色,色相在色盘上的位置如下图所示:

HSL

如果已知一个颜色的定义是hsl(200, 80%, 40%),比如用户反馈说这个色彩太暗,我们可以很容易的调整40%45%或者50%;或者用户抱怨色彩不够深,不够蓝,我们事实上又可以调整色相后者饱和度。

响应式设计

随着不同尺寸的移动设备的出现,开发者需要完成一种设计,这种设计可以在不同的设备上都正常工作。流式布局(完全按照百分比来定位,去除代码中关于尺寸的硬编码)可以在不同的较大的屏幕上很好的工作。在移动设备上,虽然流失布局也很好的显示,但是由于手机屏幕本身的限制,一个很小的百分比的元素并不真的可用。比如在桌上显示器上,20%是一个足够大的可视区域,但是在手机上可能就完全无法识别了。

因此,响应式设计应用而生,简而言之,响应式设计为每个不同尺寸的设备分别作了设计。比如在桌面上,每行可以显示4张图片,而在iPad上,每行显示两张,在手机上,每行只显示一张图片。这样可以保证对于不同尺寸的设备都提供很高的可用性。

Media Query

在CSS中,你可以知道屏幕的尺寸,并且根据尺寸的不同来应用不同的CSS文件,这个机制就是media query。比如我们可以在一个link标签中写这样的代码:

<link rel="stylesheet" media="screen and (max-width: 480px)" href="numbers-small.css" />

这个CSS会在最大视口(viewport)为480px的环境中生效。同样,我们可以在CSS代码中使用这样的表达式:

.information {
    background-color: red;
}

@media screen and (min-width: 1024px) {
    .information {
        background-color: yellow;
    }
}

所有的响应式设计都依赖于这个机制,一旦我们可以感知屏幕的尺寸,就可以为其应用不同的样式:将某些元素的宽度增大,隐藏某些元素等等。接下来我们来看一个实例,并从中学习如何在我们的页面中使用响应式设计。

一个实例

比如Twitter new face中,我们可以看到一些数字,我们可以将它改造成响应式的。

<section class="numbers">
    <ul>
        <li class="item">
            <h4>Tweets</h4>
            <p>200</p>
        </li>
        <li class="item">
            <h4>Photo/Videos</h4>
            <p>250</p>
        </li>
        <li class="item">
            <h4>Following</h4>
            <p>55</p>
        </li>
        <li class="item">
            <h4>Followers</h4>
            <p>180</p>
        </li>
    </ul>
</section>

我们在桌面浏览器中,需要将这4个数字展现在同一行上:

.numbers {
    width: 60em;
    margin: 2em auto;
    background: $post-bg-color;
    li {
        float: left;
        width: 25%;
        box-sizing: border-box;
        padding: 1.5em 3em;
        border-bottom: 3px solid transparent;
        transition: all .3s ease-in;

        h4 {
            text-transform: uppercase;
            color: $gray-text-color;
        }

        p {
            margin-top: 1em;
            font-weight: bold;
        }

        &:hover {
            border-bottom: 3px solid $twitter-color;
        }
    }
    @include clearfix;
}

numbers large

Sass在3.2之后,提供了非常方便的机制,我们可以根据不同的尺寸来应用不同的样式。首先定义一个mixin,这个mixin接受三个可能的参数,分别表示手机,平板和桌面浏览器。

@mixin respond-to($media) {
  @if $media == handhelds {
    @media only screen and (max-width: $break-small) { @content; }
  }
  @else if $media == medium-screens {
    @media only screen and (min-width: $break-small + 1) and (max-width: $break-large - 1) { @content; }
  }
  @else if $media == wide-screens {
    @media only screen and (min-width: $break-large) { @content; }
  }
}

而我们需要先定义两个break-point:

$break-small: 320px;
$break-large: 1024px;

在使用的时候,只需要指定:

li {
    float: left;
    width: 25%;

    @include respond-to(medium-screens) {
        width: 50%;
    }
    //...
}

numbers medium

对于手机屏幕:

li {
    float: left;
    width: 25%;

    @include respond-to(handhelds) {
        float: none;
        width: 100%;
    }
    //...
}

numbers small