Hey , you !


  • Home

  • Archives

A Trap When Using letter-spacing to Remove the Extra Space in chrome66.0.3359.139

Posted on 2018-06-18 | In css

Preface

As we all know there would be extra space between inline-blocks when using inline-block layout. There are lots of solutions like font-size:0,word-spacing,margin-left,letter-spacing and so on.

Choice I Made

Normally I prefer using letter-spacing because

  1. it seems ok when we assign a value which is bigger than the width of extra space(e.g. -1em).
  2. However, it won’t be okay with word-spacing and margin-left when we set bigger value like -1em.
  3. Using font-size is not convenient when we try to using em as font-size unit.

So, letter-spacing seems to be the best choice.

Well, I agree with that though I found a bug in chrome66.0.3359.139 today.

Bug I Found

Please run the code below:

<nav class="nav">
<span class="nav__text">nav1</span>
<span class="nav__text">nav2</span>
<span class="nav__text">nav3</span>
</nav>
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
color: inherit;
cursor: auto;
}
.nav {
width: 260px;
height: 100px;
background-color: pink;
color: white;
font-size: 20px;
letter-spacing: -1em;
}
.nav__text {
width: 90px;
height: 40px;
box-sizing: border-box;
border: 1px solid black;
line-height: 40px;
background-color: yellowgreen;
text-align: center;
display: inline-block;
letter-spacing: normal;
}

If you are using Chrome(test version 66.0.3359.139) or Opera(test version 53.0.2907.99), what you see might be:

If you are using Firefox(60.0.2),IE10 or Edge, what you see might be:

That’s interesting. So, I checked the mdn-letter-spacing and found this:

Specifies extra inter-character space in addition to the default space between characters. Values may be negative, but there may be implementation-specific limits. **User agents may not further increase or decrease the inter-character space in order to justify text.**

It seems that this is the reason.

So, I tested and try to find the best value for letter-spacing and it turns out that 0.3em or 0.31em is the best value.

Ending

Anyway, letter-spacing is still the best solution though we have to be more careful.

Reference

mdn-letter-spacing

Layout On Mobile

Posted on 2018-06-15 | In css

Preface

Based on first-exploration-on-mobile we can develop adaptive pages on mobile. Now, let’s do it.

Main

Basic Code

Suppose that we got the PSD which is designed according to iPhone6. So, what do we have now?

  • width:375*667
  • DPR:2

So, let’s develop according to iPhone6 first. And this is the basic code:

<head>
<meta charset="UTF-8">
<title>Document</title>
<meta name="viewport" content="width=375,initial-scale=1,user-scalable=no">
<style>
/* reset.css */

* {
margin: 0;
padding: 0;
}

html,
body {
height: 100%;
width: 100%;
}

li {
list-style: none;
}

a {
text-decoration: none;
color: inherit;
}

/* base.css */

.nav,
.nav__list {
width: 100%;
height: 100%;
}

.nav__list {
background-color: pink;
overflow: hidden;
}

.nav__item {
width: 65px;
height: 40px;
line-height: 40px;
margin: 5px;
float: left;
background-color: yellowgreen;
color: white;
}

.nav__link {
display: block;
width: 100%;
height: 100%;
text-align: center;
}

</style>
</head>

<body>
<nav class="nav">
<ul class="nav__list">
<li class="nav__item">
<a href="#" class="nav__link">nav1</a>
</li>
<li class="nav__item">
<a href="#" class="nav__link">nav2</a>
</li>
<li class="nav__item">
<a href="#" class="nav__link">nav3</a>
</li>
<li class="nav__item">
<a href="#" class="nav__link">nav4</a>
</li>
<li class="nav__item">
<a href="#" class="nav__link">nav5</a>
</li>
</ul>
</nav>
</body>

Well, I made a simple one just for test. Hope you don’t mind. If we switch to iPhone6 device, it looks okay.

Of course it will be terrible if we switch to other devices. For example, iPhone6 Plus:

We got extra space on the right because the width of iPhone6 Plus is 414px which is wider than 375px in iPhone6.

Please notice that we have used 65px, 40px and 5px in the css. Those values should be zoomed according to the device-width.

So, here is the way how we fix this:

Adaptive by rem and js

We need do some transform to use rem. On iPhone6 with 375px and DPR:2,

  1. We can use 65px, 40px, 5px with:
<meta name="viewport" content="width=375,initial-scale=1,user-scalable=no">
  1. We can also use 130px, 80px, 10px with:
<meta name="viewport" content="width=750,initial-scale=0.5">
  1. Further on, if we calculate rem by (device-width*DPR)/10:
<!--width=750=375*DPR,initial-scale=0.5=1/DPR-->
<meta name="viewport" content="width=750,initial-scale=0.5">
html {
/*75px = device-width * DPR / 10 */
font-size: 75px;
}
body {
/*32px = 16px * DPR */
font-size: 32px;
}

Those numbers would be:

  • 65px => (65/375) * device-width => (65/375) * 10rem
  • 40px => (40/375) * device-width => (40/375) * 10rem
  • 5px => (5/375) * device-width => (5/375) * 10rem

In fact, options above will get the same effect. But for option3, we make everything connected with DPR and device-width.

So, here is the basic logic to be adaptive:

  1. We use the device-width*DPR/10 to calculate rem and set it on html.
  2. We set (initial font-size * DPR) on body.
  3. For font-size set with px, we have to set different font-size for different DPR by @media/-webkit-device-pixel-ratio or plugins(e.g. less).
  4. We update meta by setting the width as device-width*DPR and initial-scale as 1/DPR which would make the page render in device-width*DPR and then scale to device-width.

Well, you can just see the code:

<head>
<meta charset="UTF-8">
<title>Document</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script>
document.addEventListener(
'DOMContentLoaded',
() => {
let dpr = window.devicePixelRatio
let html = document.documentElement
let body = document.body
let deviceWidth = window.innerWidth || html.clientWidth
html.style.fontSize = (deviceWidth * dpr) / 10 + 'px'
html.setAttribute('data-dpr', dpr)
body.style.fontSize = 'initial'
body.style.fontSize = parseInt(getComputedStyle(body).fontSize) * dpr + 'px'
let metaViewport = document.querySelector('meta[name=viewport]')
metaViewport.setAttribute(
'content',
`width=${dpr * deviceWidth},initial-scale=${1 / dpr}`
)
},
{ capture: true },
true
)
</script>
<link rel="stylesheet/less" href="./less.less">
<script src="http://cdnjs.cloudflare.com/ajax/libs/less.js/3.0.2/less.min.js"></script>
</head>

base.css in less.less

/* base.css */
.px2px(@name, @px) {
@{name}: round(@px) * 1px;
[data-dpr='2'] & {
@{name}: round(@px*2) * 1px;
}
// for mx3
[data-dpr='2.5'] & {
@{name}: round(@px * 2.5) * 1px;
}
//for Pixel2
[data-dpr='2.625'] & {
@{name}: round(@px * 2.625) * 1px;
}
// for XiaoMi note
[data-dpr='2.75'] & {
@{name}: round(@px * 2.75) * 1px;
}
[data-dpr='3'] & {
@{name}: round(@px * 3) * 1px;
}
//for Pixel2 XL
[data-dpr='3.5'] & {
@{name}: round(@px * 3.5) * 1px;
}
// for Samsung note4
[data-dpr='4'] & {
@{name}: @px * 4px;
}
}

.nav {
//suggest use em not px otherwise have to set different font-size for different DPR
.px2px(font-size, 16px);
overflow: hidden;
width: 100%;
}

.nav__list {
// width: 110%;
width: 100%;
height: 100%;
// for inline-block
// white-space: nowrap;
overflow: hidden;
background-color: pink;
// letter-spacing: -0.3em;
}

.nav__item {
float: left;
// display: inline-block;
// letter-spacing: normal;
color: white;
background-color: yellowgreen;

width: 65/375 * 10rem;
height: 40/375 * 10rem;

margin: 5/375 * 10rem;
line-height: 40/375 * 10rem;
}

.nav__link {
display: block;

width: 100%;
height: 100%;

text-align: center;
}

Code above will work in Apple’s product. However, when we switch to Pixel 2 we will find this:

After a little calculation, I find that the total width of 5 navs is 1078.85 while width of .nav__list is 1078. Well, I think it is reasonable though I hate this.

In this case, we have to do something else to fix this. For example:

  • Set margin-right:auto on the last nav.
  • Set wider width of .nav__list and set overflow:hidden on .nav.
  • Use width:20% and padding instead of margin.
  • Other css tricks.

Anyway, this is the previous solution for adaptation on mobile before appearance of vw,flex and grid layout.

Let’s make a conclusion:

  • principle
  1. Set different font-size for different DPR by less.
  2. Use viewport scale to solve the problem of 1px on border and font-size.
  3. Make the width and height of element connect with device-width by rem.
  • pro
  1. Fix 1px problem on border because we set 1px on device*DPR.
  • con
  1. We have to set lots of font-size based on DPR every time we want to set font-size with px. Though we can simplify our job by preprocessor(e.g. less). However, how we going to do with more and more DPR?
.px2px(@name, @px) {
@{name}: round(@px) * 1px;
[data-dpr='2'] & {
@{name}: round(@px*2) * 1px;
}
// for mx3
[data-dpr='2.5'] & {
@{name}: round(@px * 2.5) * 1px;
}
//for Pixel2
[data-dpr='2.625'] & {
@{name}: round(@px * 2.625) * 1px;
}
// for XiaoMi note
[data-dpr='2.75'] & {
@{name}: round(@px * 2.75) * 1px;
}
[data-dpr='3'] & {
@{name}: round(@px * 3) * 1px;
}
//for Pixel2 XL
[data-dpr='3.5'] & {
@{name}: round(@px * 3.5) * 1px;
}
// for Samsung note4
[data-dpr='4'] & {
@{name}: @px * 4px;
}
}

Write more and more? That’s not a good idea.

If we use em instead it will not be a problem because we already set the initial font-size * DPR on body.

  1. Decimals appear which may cause a little problem in layout.

Adaptive by vw

Before we start with vw let’s take a look at the rem and js solution. So, do we have a better solution for:

  1. border 1px problem? I don’t think so.
  2. make the width and height of element connect with device-width? Yes, we do have vw, vh even vmin and vmax.

And for the con of the rem and js solution:

  1. In this case, we can use rem instead of em because rem works more like px than em
  2. Can we avoid the decimals ? May be not with vw but won’t be worse than rem.

Anyway, here is the code:

<head>
<meta charset="UTF-8">
<title>Document</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script>
document.addEventListener(
'DOMContentLoaded',
() => {
let dpr = window.devicePixelRatio
let html = document.documentElement
let body = document.body
let deviceWidth = window.innerWidth || html.clientWidth
html.setAttribute('data-dpr', dpr)
html.style.fontSize = 'initial'
html.style.fontSize = parseInt(getComputedStyle(html).fontSize) * dpr + 'px'
let metaViewport = document.querySelector('meta[name=viewport]')
metaViewport.setAttribute(
'content',
`width=${dpr * deviceWidth},initial-scale=${1 / dpr}`
)
},
{ capture: true },
true
)
</script>
<link rel="stylesheet/less" href="./less.less">
<script src="http://cdnjs.cloudflare.com/ajax/libs/less.js/3.0.2/less.min.js"></script>
</head>

base.css in less.less

/* base.css */
.nav {
font-size: 1rem;
width: 100%;
}

.nav__list {
width: 100%;
height: 100%;
overflow: hidden;
background-color: pink;
}

.nav__item {
float: left;
color: white;
background-color: yellowgreen;

width: 65/375 * 100vw;
height: 40/375 * 100vw;

margin: 5/375 * 100vw;
line-height: 40/375 * 100vw;
}

.nav__link {
display: block;

width: 100%;
height: 100%;

text-align: center;
}

I tested every device in chrome device-mode. They all work.

However, there are still decimals which might bite you someday.

Anyway, let’s make a conclusion:

  • principle
  1. Make the font-size connect with dpr by rem.
  2. Use viewport scale to solve the problem of 1px on border and font-size.
  3. Make the width and height of element connect with device-width by vw.
  • pro
  1. Border 1px problem is solved as before.
  2. Decimals problem will be a litter better than rem and js solution.
  • con
  1. Decimals still exist.

Adaptive by vw and flex

Solution with vw almost perfect except the decimals. So, how we are going to fix that? Flex gave us a choice.

We can leave the decimals to browser by flex.

/*part of css which has been modified*/
.nav__list {
width: 100%;
height: 100%;
overflow: hidden;
background-color: pink;
display: flex; /*new added*/
justify-content: space-around; /*new added*/
}

.nav__item {
color: white;
background-color: yellowgreen;
flex: 0 0 auto; /*new added*/

width: 65/375 * 100vw;
height: 40/375 * 100vw;

line-height: 40/375 * 100vw;
}

If you don’t know something about flex, please check MDN. Anyway, we can leave decimals to browser. That is the most exciting part.

Well, let’s make a conclusion:

  • principle
  1. Make the font-size connect with dpr by rem.
  2. Use viewport scale to solve the problem of 1px on border and font-size.
  3. Make the width and height of element connect with device-width by vw.
  4. Leave margin to browser by flex.
  • pro
  1. Border 1px problem is solved as before.
  2. Decimals problem will be better than vw.
  • con
  1. Decimals still exist but only has a little influence.

Ending

Anyway, the decimals problem always exist because of different sizes of mobiles. But the more new API we have, the less influence it will have.

Besides, js above is not perfect. Here is the current version I am using.

And the last thing is we didn’t handle the problem with img. Maybe you should seek some help from retina.js.

Reference

first-exploration-on-mobile

@media/-webkit-device-pixel-ratio

lib-flexible

retina.js

First Exploration on Mobile

Posted on 2018-06-14 | In css

Preface

Recently, I have been working on mobile development. Before this I have heard something strange but I didn’t pay attention to. Now, I have to figure out though there is a lot of stuff. I am trying to make it clear as possible as I can but I am not sure content below is completely right.

Basic concepts

Css Pixels, Physical Pixels and DPR

First, let’s start with demo:

<div class="cont">
<div class="left">left</div>
<div class="right">right</div>
</div>
* {
margin: 0;
padding: 0;
}

html,
body {
height: 100%;
width: 100%;
}

.cont {
background: pink;
overflow: auto;
height: 100%;
width: 100%;
}

.left {
float: left;
width: 50%;
height: 20%;
background: lightblue;
}

.right {
float: right;
width: 50%;
height: 20%;
background: yellowgreen;
}

Before switching to device mode, make sure you don’t have code starts with <meta name="viewport" ... in your head tag.

Normally, we will find everything seems ok when we switch to mobile. For example, Let’s switch to iPhone6.

According to chrome-devtools, width 375 and height 667 is css pixels. And before we keep going , we need to figure out some terms first including css pixels, physical pixels and DPR. Here is some related readings:

  • converting-between-physical-pixels-and-css-pixels

  • what-exactly-is-device-pixel-ratio

  • mdn-devicePixelRatio

After reading those stuff, I am still confused. But I can figure something out at least. Well, here is the point based on iPhone6:

  • width 375 and height 667 is css pixels

  • DPR is 2

  • DPR = physical pixels/css pixels. So physical pixels is 750*1334

Layout Viewport and Visual Viewport

Ok, let’s keep going. If you calculate the dimensions of viewport, you will get some strange values like below:

document.documentElement.clientWidth //980
document.documentElement.clientHeight //1743

Maybe you thought that the value should be 375*667 or 750*1334. However, it doesn’t. The result 980*1743 is the dimensions of layout viewport. Well, before keep going we need to know something about layout viewport and visual viewport. Here is the related articles:

  • quirksmode-viewports

  • quirksmode-viewports2

  • quirksmode-viewports-app

Based on the articles above we can be sure of something about layout viewport and visual viewport.

  • layout viewport is the area that the browser uses to calculate the dimensions of elements with percentual width.

  • visual viewport is the part of the page that’s currently shown on-screen. The user may scroll to change the part of the page he sees, or zoom to change the size of the visual viewport.

  • Zoom doesn’t change the size of layout viewport but visual viewport.

  • The size of visual viewport <= the size of layout viewport.

  • Both layout viewport and visual viewport are measured by css pixels.

  • Many mobile browsers initially show any page in fully zoomed-out mode to show the complete content of the site. Thus layout viewport is equal to the visual viewport at that moment.

And in this case of iPhone6, here is something we can be sure of:

  • layout viewport is 980*1743

  • visual viewport is also 980*1743

  • The mobile browser show the page in fully zoomed-out mode to show the complete content of the site.

Meta Tag

Now, we know what happened when we switch to mobile mode. Also, we know why the size of font becomes smaller in visual.

However, we would want the layout viewport to be 375*667 or 750*1334 if we want to build a adaptive website. Fortunately, we can control the size of layout viewport by tag meta. For instance,

<meta name="viewport" content="width=375,initial-scale=1,user-scalable=no">

You can find the doc of meta in mdn-meta and viewport in mdn-viewport_meta_tag. In the code above, we define 375px for the width of layout viewport. Here, 375px is css pixels. Also, you can prove it by:

document.documentElement.clientWidth //375
document.documentElement.clientHeight //667

So, any change? Yes. The change is the font-size is bigger in visual than before. Before we change the text is too small to read.

We can keep testing by change the width in meta. For example, let’s try 750:

<meta name="viewport" content="width=750,initial-scale=1,user-scalable=no">

Look what we got:

This makes sense. However, if you remove user-scalable=no in the meta the browser will again enter into the fully zoomed-out mode. In that case, you will find the font-size is smaller and the whole page is showed in the screen which make the effect of initial-scale=1 disappear.

To be compatible with mobiles with different sizes we need to switch the number to device-width:

<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">

It is easy to understand, I think. Until now, it seems that we can be compatible with mobiles. Is that true?

Adaptive Layout

In the case above, we set width:50% for the .left and .right box. So, they could be adaptive. If we use px the page will not be adaptive because the size of viewport varies between mobiles. So, we have two choices

  1. Use different size according to the size of viewport
  2. Use percentage always

Option2 will work if our page is simple enough. In most instances, it will bring a disaster because of nested elements. One modification in parent will bring changes to all descendant elements.

The problem of option1 is if we can avoid writing so many media queries codes to adapt to different sizes of viewport. For example:

/* ... other codes for .cont with different sizes */
@media (max-width: 800px) {
.cont {
width: 200px;
}
}

@media (max-width: 400px) {
.cont {
width: 100px;
}
}
/*... other codes for .cont with different sizes */

Actually, we can avoid these codes. The key is rem. For example, code above can be simplified like:

html {
font-size: 80px;
}

.cont {
width: 2.5rem;
}

/* no more codes for .cont with different sizes */

When the size of viewport is 400px, we just need to update the font-size of html with 40px. That is easy with js. Seems wonderful with rem? huh?

You can use rem everywhere and js just run one time. Here is the related lib lib-flexible.

Latest Adaptive Layout

Basically, there is no big problem with rem. Well, you might realize that we can totally use vw instead. That’s correct! The compatibility has become better for most developers to use. The same thing happen to flex and grid layout. Until 2018.6, flex is a choice but we still need to wait for grid or look for some polyfill.

However, there is still some little problems when using vw or rem layout.

  • We still use px for font-size and border.

I think it is easy to understand because decimals would appear. In that case, we may get 15px or 17px instead of 16px. At the same time, border may disappear because of some numbers like 0.9px.

  • Develop with plugins

For instance, if we are developing based on iPhone6 whose width is 375px in css pixel while the designer gives us a PSD whose width is 750px, how we are going to do? Calculate the div with 75px and transform to 10% and write 10vw? So we calculate and write it one by one? That’s terrible.

We can just write 75px and other numbers and leave the transform jobs to plugins(e.g. less,postcss.etc.).

Problems Caused by DPR

Blurry or Sharp in Different DPR Device

Until now, we haven’t talked about DPR which we mentioned in the beginning. As mdn-devicepixelratio says:

In simpler terms, this tells the browser how many of the screen’s actual pixels should be used to draw a single CSS pixel.

On the desktop or before the appearance of Retina display, DPR is always equal to 1. However, things became complicated after that. At present, there is a lot of DPR on the market which varies from 1 to 4. Sometimes, there comes 1.5 or 2.75. Anyway, we will talk about it later.

We can always get DPR by window.devicePixelRatio. The problem is that on retina screens or device whose DPR more than 1 images will become blurry. Of course we will try to avoid that. So, we have to figure out what happened to those mobiles with bigger DPR.

According to towards-retina-web,

A bitmap pixel is the smallest unit of data in a raster image (PNG, JPG, GIF, etc). Each pixel contains information on how it is to be displayed, including its position in the image’s coordinate system and its color.

Beside its raster resolution, an image on the Web has an abstract size, defined in CSS pixels. The browser squeezes or stretches the image based on its CSS height or width during the rendering process.

When a raster image is displayed at full size on a standard-density display, 1 bitmap pixel corresponds to 1 device pixel, resulting in a full-fidelity representation. Because a bitmap pixel can’t be further divided, it gets multiplied by four on Retina displays(assuming DPR is 2) to preserve the same physical size of the image, losing detail along the way.

For example, we got iPhone6 whose DPR is 2. And img we have is:

<img src="example.png" width="2" height="2">

Assume that size of example.png is 2*2 and the picture is like:

With DPR of 2, the browser has to use 2 device pixels to draw a single css pixel. So img with 2*2 css pixels will be drew by 4*4 device pixel which means example.png with 2*2 bitmap pixels will be drew by 4*4 device pixel. So, 4 bitmap pixels of example.png will be drew by 16 device pixels which means 1 bitmap pixel will be drew by 4 device pixels.

Let’s move on, normally what we want might be:

However, actual result will be:

The result happens because 1 bitmap pixel can’t be further divided into 4 device pixels. That’s what @smashingmagazine says:

A bitmap pixel is the smallest unit of data in a raster image (PNG, JPG, GIF, etc).

Because a bitmap pixel can’t be further divided, it gets multiplied by four on Retina displays(assuming DPR is 2) to preserve the same physical size of the image, losing detail along the way.

You can also find another example in mdn-devicepixelratio.

So, how can we fix this? Basically, choices we have are listed as below:

  1. Change the size of img, for example:
<img src="example.png" width="1" height="1">

In this case, img with 1*1 css pixel will be drew by 2*2 device pixels which means example.png with 2*2 bitmap pixels will be divided by 2*2 device pixels. Hence, 1 bitmap pixel corresponds to 1 device pixel.

We might not use this alone because the layout would be affected.

  1. Replace example.png with 4*4 and keep the original img with 2*2 css pixels, same explanation as above.

In this case, we might do this by changing the src attribute in the img. For example, update the src search param ?v=2*2 with ?v=4*4 or change from example.png to example@2x.png. retinajs will help you do this job.

  1. Use icon-font instead of raster image.

  2. Use svg instead.

Actually, what you need to care about is not only img but also background-image. So, option1 and option2 will be not convenient without any other help(e.g. retinajs).

Also, with DPR smaller than 2 we need to switch size back. If not, image will become sharp which is the opposite process of blur. For instance:

Compare with the process of blur:

Option3 and option4 would be easier. And with the help of iconfont, things can be easier and easier when you choose svg.

Personally, I would choose option4 in daily work. For more information about the options, click towards-retina-web.

Problems about Border

Assume that we are using iPhone6 whose DPR is 2, border with 1 css pixel will be drew with 2 device pixels. Does that matter?

Yeah, sometimes that make the border thicker in visual. When we set 1px on border, actually what we might want is to set the thinnest border that device support. Normally, on desktop browser 1px is the thinnest. So in this case, you might need to talk with designer if he wants the thinnest border(e.g. 0.5px) or still 1px.

Anyway, there is lots of ways if we want to set the thinnest border. So, I wrote another blog to talk about that. Please check border on mobile.

Ending

I have written too much about the basic knowledge about layout on mobile. However, there are still lots of problems I haven’t mentioned. Anyway, I will write them in other blogs.

Reference

quirksmode-viewports

quirksmode-viewports2

quirksmode-viewports-app

a_pixel_is_not

mdn-meta

mdn-viewport_meta_tag

mdn-devicepixelratio

specs-mediaqueries-3

chrome-devtools

what-exactly-is-device-pixel-ratio

converting-between-physical-pixels-and-css-pixels

towards-retina-web

iconfont

retinajs

lib-flexible

Chinese:

移动端适配方案(上)

移动端适配方案(下)

移动端高清、多屏适配方案

使用 flexible 实现手淘 h5 页面的终端适配

如何在 vue 项目中使用 vw 实现移动端适配

再聊移动端页面的适配

1…141516…18

xianshenglu

54 posts
14 categories
142 tags
© 2023 xianshenglu
Powered by Hexo
|
Theme — NexT.Muse v5.1.4