部落格重構 Phase 1

Introduction

為了支援 SHOPLINE 的部落格重構 Phase 1,對於有開啟 Layout Engine的店家,需調整代碼以支援以下新功能:

  1. 新增部落格麵包屑
    1. 此文件異動,將由 SHOPLINE 統一為 Layout Engine 店家更新,您無需調整代碼,此處僅列出更新內容
  2. 套用 CKEditor 5 樣式
  3. 優化部落格圖片載入效能 (image lazyload、隨螢幕寬度展示對應圖片大小、減少 mobile 端下載圖片的大小)

1. 新增部落格麵包屑

A. 配置 Locales

en.json

步驟一:找出要修改的程式碼

找到以下 JSON 開頭是 "products": { ... } 的區塊

步驟二:將找到的 "products": { ... } 區塊下面加入以下 JSON

,
"blog": {
  "home": "Home",
  "blog_list_page": "Blog list page"
}

vi.json

沒有 vi.json 語系檔案可跳過以下步驟

步驟一:找出要修改的程式碼

找到以下 JSON 開頭是 "products": { ... } 的區塊

步驟二:將找到的 "products": { ... } 區塊下面加入以下 JSON

,
"blog": {
  "home": "Home",
  "blog_list_page": "Blog list page"
}

zh-hant.json

步驟一:找出要修改的程式碼

找到以下 JSON 開頭是 "products": { ... } 的區塊

步驟二:將找到的 "products": { ... } 區塊下面加入以下 JSON

,
"blog": {
  "home": "Home",
  "blog_list_page": "部落格列表"
}

B. 新增 blog_breadcrumb.liquid

<div class="blog-breadcrumb"> <div class="block-inner"> <a class="Label" href="{{ root_url }}">{{ 'shopline_translations.themes.blog.home' | translate }}</a> / <a class="Label" href="/blog/posts">{{ 'shopline_translations.themes.blog.blog_list_page' | translate }}</a> {% unless posts %} / <span class="Label">{{ post.title }}</span> {% endunless %} </div> </div> {% style %} body.posts .blog-breadcrumb { background-color: #F7F7F7; color: #999; font-size: 13px; } body.posts .blog-breadcrumb .block-inner { width: 1400px; max-width: 100%; margin: 0 auto; padding: 10px 20px; } body.posts .blog-breadcrumb .block-inner .Label { color: #999; position: relative; } body.posts .blog-breadcrumb .block-inner .Label:after { content: ""; position: absolute; left: 50%; right: 50%; bottom: -3px; border-bottom-width: 2px; border-bottom-style: solid; } body.posts .blog-breadcrumb .block-inner .Label:hover:after { left: 0; right: 0; } {% endstyle %}

2. 套用 CKEditor 5 樣式

A. 更新 post.liquid

步驟一:找到 <div class="Post-content">{{ post.content }}</div>的程式碼

步驟二:將其改成下列程式碼

<div class="Post-content">
  {% assign rich_content_body = post.content %}
	{% include 'rich_content' %}
</div>

步驟三:請到文件底部

步驟四:新增這段支援 CKEditor 5 樣式的 CSS

{% style %} /* * CKEditor 5 (v41.3.1) content styles. * Generated on Tue, 30 Apr 2024 10:30:42 GMT. * For more information, check out https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/content-styles.html */ body.posts.show .Post .Post-content :root { --ck-color-image-caption-background: hsl(0, 0%, 97%); --ck-color-image-caption-text: hsl(0, 0%, 20%); --ck-color-mention-background: hsla(341, 100%, 30%, 0.1); --ck-color-mention-text: hsl(341, 100%, 30%); --ck-color-selector-caption-background: hsl(0, 0%, 97%); --ck-color-selector-caption-text: hsl(0, 0%, 20%); --ck-image-style-spacing: 1.5em; --ck-inline-image-style-spacing: calc(var(--ck-image-style-spacing) / 2); } body.posts.show .Post .Post-content .table > figcaption { display: table-caption; caption-side: top; word-break: break-word; text-align: center; color: var(--ck-color-selector-caption-text); background-color: var(--ck-color-selector-caption-background); padding: 0.6em; font-size: 0.75em; outline-offset: -1px; } body.posts.show .Post .Post-content .table .ck-table-resized { table-layout: fixed; } body.posts.show .Post .Post-content .table table { overflow: hidden; } body.posts.show .Post .Post-content .table td, body.posts.show .Post .Post-content .table th { overflow-wrap: break-word; position: relative; } body.posts.show .Post .Post-content .table { margin: 0.9em auto; display: table; } body.posts.show .Post .Post-content .table table { border-collapse: collapse; border-spacing: 0; width: 100%; height: 100%; border: 1px double hsl(0, 0%, 70%); } body.posts.show .Post .Post-content .table table td, body.posts.show .Post .Post-content .table table th { min-width: 2em; padding: 0.4em; border: 1px solid hsl(0, 0%, 75%); } body.posts.show .Post .Post-content .table table th { font-weight: bold; background: hsla(0, 0%, 0%, 0.05); } body.posts.show .Post .Post-content[dir=rtl] .table th { text-align: right; } body.posts.show .Post .Post-content[dir=ltr] .table th { text-align: left; } body.posts.show .Post .Post-content .media { clear: both; margin: 0.9em 0; display: block; min-width: 15em; } body.posts.show .Post .Post-content img.image_resized { height: auto; } body.posts.show .Post .Post-content .image.image_resized { max-width: 100%; display: block; box-sizing: border-box; } body.posts.show .Post .Post-content .image.image_resized img { width: 100%; } body.posts.show .Post .Post-content .image.image_resized > figcaption { display: block; } body.posts.show .Post .Post-content .image { display: table; clear: both; text-align: center; margin: 0.9em auto; min-width: 50px; } body.posts.show .Post .Post-content .image img { display: block; margin: 0 auto; max-width: 100%; min-width: 100%; height: auto; } body.posts.show .Post .Post-content .image-inline { /* * Normally, the .image-inline would have "display: inline-block" and "img { width: 100% }" (to follow the wrapper while resizing).; * Unfortunately, together with "srcset", it gets automatically stretched up to the width of the editing root. * This strange behavior does not happen with inline-flex. */ display: inline-flex; max-width: 100%; align-items: flex-start; } body.posts.show .Post .Post-content .image-inline picture { display: flex; } body.posts.show .Post .Post-content .image-inline picture, body.posts.show .Post .Post-content .image-inline img { flex-grow: 1; flex-shrink: 1; max-width: 100%; } body.posts.show .Post .Post-content .image > figcaption { display: table-caption; caption-side: bottom; word-break: break-word; color: var(--ck-color-image-caption-text); background-color: var(--ck-color-image-caption-background); padding: 0.6em; font-size: 0.75em; outline-offset: -1px; } body.posts.show .Post .Post-content ol { list-style-type: decimal; } body.posts.show .Post .Post-content ol ol { list-style-type: lower-latin; } body.posts.show .Post .Post-content ol ol ol { list-style-type: lower-roman; } body.posts.show .Post .Post-content ol ol ol ol { list-style-type: upper-latin; } body.posts.show .Post .Post-content ol ol ol ol ol { list-style-type: upper-roman; } body.posts.show .Post .Post-content ul { list-style-type: disc; } body.posts.show .Post .Post-content ul ul { list-style-type: circle; } body.posts.show .Post .Post-content ul ul ul { list-style-type: square; } body.posts.show .Post .Post-content ul ul ul ul { list-style-type: square; } body.posts.show .Post .Post-content ul, body.posts.show .Post .Post-content ol { padding: 0; margin: 0; padding-inline-start: 40px; } body.posts.show .Post .Post-content .image-style-block-align-left, body.posts.show .Post .Post-content .image-style-block-align-right { max-width: calc(100% - var(--ck-image-style-spacing)); } body.posts.show .Post .Post-content .image-style-align-left, body.posts.show .Post .Post-content .image-style-align-right { clear: none; } body.posts.show .Post .Post-content .image-style-side { float: right; margin-left: var(--ck-image-style-spacing); max-width: 50%; } body.posts.show .Post .Post-content .image-style-align-left { float: left; margin-right: var(--ck-image-style-spacing); } body.posts.show .Post .Post-content .image-style-align-center { margin-left: auto; margin-right: auto; } body.posts.show .Post .Post-content .image-style-align-right { float: right; margin-left: var(--ck-image-style-spacing); } body.posts.show .Post .Post-content .image-style-block-align-right { margin-right: 0; margin-left: auto; } body.posts.show .Post .Post-content .image-style-block-align-left { margin-left: 0; margin-right: auto; } body.posts.show .Post .Post-content p + .image-style-align-left, body.posts.show .Post .Post-content p + .image-style-align-right, body.posts.show .Post .Post-content p + .image-style-side { margin-top: 0; } body.posts.show .Post .Post-content .image-inline.image-style-align-left, body.posts.show .Post .Post-content .image-inline.image-style-align-right { margin-top: var(--ck-inline-image-style-spacing); margin-bottom: var(--ck-inline-image-style-spacing); } body.posts.show .Post .Post-content .image-inline.image-style-align-left { margin-right: var(--ck-inline-image-style-spacing); } body.posts.show .Post .Post-content .image-inline.image-style-align-right { margin-left: var(--ck-inline-image-style-spacing); } body.posts.show .Post .Post-content code { background-color: hsla(0, 0%, 78%, 0.3); padding: 0.15em; border-radius: 2px; color: inherit; } body.posts.show .Post .Post-content .text-tiny { font-size: 0.7em; } body.posts.show .Post .Post-content .text-small { font-size: 0.85em; } body.posts.show .Post .Post-content .text-big { font-size: 1.4em; } body.posts.show .Post .Post-content .text-huge { font-size: 1.8em; } {% endstyle %}

B. 更新 rich_style.liquid

步驟一:如果你的商店沒有 rich_content.liquid 檔案,請複製以下程式碼到代碼片段新增檔案,有的話則跳過此步驟

{% capture custom_html %} <style> {% include 'rich_style' %} </style> {% endcapture %} {{ rich_content_body | rich_content_format: custom_html: custom_html, }}

步驟二:如果你的商店沒有 rich_style.liquid 檔案,請複製以下程式碼到代碼片段新增檔案,有的話則跳過此步驟

h1, h2, h3, h4, h5, h6 { font-weight: 500; padding: 0; margin: 0; text-align: inherit; text-transform: none; font-family: inherit; } h1 { text-transform: uppercase; font-size: calc(28px * var(--font-size-paragraph, 1)); line-height: 1.24; margin-top: 20px; } h2 { font-size: calc(32px * var(--font-size-paragraph, 1)); padding: 10px 0px 20px 0px; } h1, h2 { padding: 10px 0px 20px 0px; margin-bottom: 0px; letter-spacing: 2px; } h3 { font-size: calc(24px * var(--font-size-paragraph, 1)); } h4 { font-size: calc(18px * var(--font-size-paragraph, 1)); } h5 { font-size: calc(16px * var(--font-size-paragraph, 1)); } h6 { font-size: calc(13px * var(--font-size-paragraph, 1)) } p { margin: 0; padding: 0; line-height: 20px; } ul, ol { list-style-position: outside; padding-left: 40px; margin: 0 0 10px 0; } ul ul, ul ol, ol ul, ol ol { margin: 0; } a { text-decoration: none; color: #337ab7; } img, iframe { max-width: 100%; } img { vertical-align: middle; } table { border-collapse: collapse; border-spacing: 0; } th { text-align: left; } td, th { padding: 0; } hr { height: 0; margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eeeeee; border-color: hsla(var(--page-background-h, 0deg), var(--page-background-s, 0%), 80%, 0.5); } .ck-video-widget, .responsive-video { position: relative; } .responsive-container { display: block; width: 100%; height: auto; padding: 28%; } .res-iframe iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } {% if page.identifier == 'text_page' or page.identifier == '' %} a { color: var(--page-text-link, #3493fb); } p { line-height: 25px; } h1 { font-weight: inherit; font-weight: 28px; } ul, ol { padding-left: 56px; margin-bottom: 0; } {% endif %} {% if page.identifier == 'post_detail' %} * { line-height: 1.5; } p { margin-bottom: 20px; line-height: 1.5; } ol { margin: 15px 15px 15px 0; } ul { padding-left: 40px; } ul ul, ol ul, ul ol, ol ol { margin-top: 0; margin-bottom: 0; } a { color: var(--page-text-link, #3493fb); } h1, h2, h3, h4, h5, h5, h6 { line-height: inherit; } h1 { font-weight: inherit; font-size: 28px; } {% endif %} {% if page.identifier == 'product_detail' %} h1 { font-weight: inherit; font-size: 28px; } {% endif %}

步驟三:請到 rich_style.liquid 檔案底部

步驟四:新增這段支援 CKEditor 5 樣式的 CSS

{% if page.identifier == 'post_detail' %} /* * CKEditor 5 (v41.3.1) content styles. * Generated on Tue, 30 Apr 2024 10:30:42 GMT. * For more information, check out https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/content-styles.html */ :root { --ck-color-image-caption-background: hsl(0, 0%, 97%); --ck-color-image-caption-text: hsl(0, 0%, 20%); --ck-color-mention-background: hsla(341, 100%, 30%, 0.1); --ck-color-mention-text: hsl(341, 100%, 30%); --ck-color-selector-caption-background: hsl(0, 0%, 97%); --ck-color-selector-caption-text: hsl(0, 0%, 20%); --ck-image-style-spacing: 1.5em; --ck-inline-image-style-spacing: calc(var(--ck-image-style-spacing) / 2); } /* @ckeditor/ckeditor5-table/theme/tablecaption.css */ .table>figcaption { display: table-caption; caption-side: top; word-break: break-word; text-align: center; color: var(--ck-color-selector-caption-text); background-color: var(--ck-color-selector-caption-background); padding: .6em; font-size: .75em; outline-offset: -1px; } /* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */ .table .ck-table-resized { table-layout: fixed; } /* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */ .table table { overflow: hidden; } /* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */ .table td, .table th { overflow-wrap: break-word; position: relative; } /* @ckeditor/ckeditor5-table/theme/table.css */ .table { margin: 0.9em auto; display: table; /* bootstrap 3 table css */ width: 100%; } /* @ckeditor/ckeditor5-table/theme/table.css */ .table table { border-collapse: collapse; border-spacing: 0; width: 100%; height: 100%; border: 1px double hsl(0, 0%, 70%); } /* @ckeditor/ckeditor5-table/theme/table.css */ .table table td, .table table th { min-width: 2em; padding: .4em; border: 1px solid hsl(0, 0%, 75%); } /* @ckeditor/ckeditor5-table/theme/table.css */ .table table th { font-weight: bold; background: hsla(0, 0%, 0%, 5%); } /* @ckeditor/ckeditor5-table/theme/table.css */ &[dir="rtl"] .table th { text-align: right; } /* @ckeditor/ckeditor5-table/theme/table.css */ &[dir="ltr"] .table th { text-align: left; } /* @ckeditor/ckeditor5-media-embed/theme/mediaembed.css */ .media { clear: both; margin: 0.9em 0; display: block; min-width: 15em; } /* @ckeditor/ckeditor5-image/theme/imageresize.css */ img.image_resized { height: auto; } /* @ckeditor/ckeditor5-image/theme/imageresize.css */ .image.image_resized { max-width: 100%; display: block; box-sizing: border-box; } /* @ckeditor/ckeditor5-image/theme/imageresize.css */ .image.image_resized img { width: 100%; } /* @ckeditor/ckeditor5-image/theme/imageresize.css */ .image.image_resized>figcaption { display: block; } /* @ckeditor/ckeditor5-image/theme/image.css */ .image { display: table; clear: both; text-align: center; margin: 0.9em auto; min-width: 50px; } /* @ckeditor/ckeditor5-image/theme/image.css */ .image img { display: block; margin: 0 auto; max-width: 100%; min-width: 100%; height: auto; } /* @ckeditor/ckeditor5-image/theme/image.css */ .image-inline { /* * Normally, the .image-inline would have "display: inline-block" and "img { width: 100% }" (to follow the wrapper while resizing).; * Unfortunately, together with "srcset", it gets automatically stretched up to the width of the editing root. * This strange behavior does not happen with inline-flex. */ display: inline-flex; max-width: 100%; align-items: flex-start; } /* @ckeditor/ckeditor5-image/theme/image.css */ .image-inline picture { display: flex; } /* @ckeditor/ckeditor5-image/theme/image.css */ .image-inline picture, .image-inline img { flex-grow: 1; flex-shrink: 1; max-width: 100%; } /* @ckeditor/ckeditor5-image/theme/imagecaption.css */ .image>figcaption { display: table-caption; caption-side: bottom; word-break: break-word; color: var(--ck-color-image-caption-text); background-color: var(--ck-color-image-caption-background); padding: .6em; font-size: .75em; outline-offset: -1px; } /* @ckeditor/ckeditor5-list/theme/list.css */ ol { list-style-type: decimal; } /* @ckeditor/ckeditor5-list/theme/list.css */ ol ol { list-style-type: lower-latin; } /* @ckeditor/ckeditor5-list/theme/list.css */ ol ol ol { list-style-type: lower-roman; } /* @ckeditor/ckeditor5-list/theme/list.css */ ol ol ol ol { list-style-type: upper-latin; } /* @ckeditor/ckeditor5-list/theme/list.css */ ol ol ol ol ol { list-style-type: upper-roman; } /* @ckeditor/ckeditor5-list/theme/list.css */ ul { list-style-type: disc; } /* @ckeditor/ckeditor5-list/theme/list.css */ ul ul { list-style-type: circle; } /* @ckeditor/ckeditor5-list/theme/list.css */ ul ul ul { list-style-type: square; } /* @ckeditor/ckeditor5-list/theme/list.css */ ul ul ul ul { list-style-type: square; } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-style-block-align-left, .image-style-block-align-right { max-width: calc(100% - var(--ck-image-style-spacing)); } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-style-align-left, .image-style-align-right { clear: none; } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-style-side { float: right; margin-left: var(--ck-image-style-spacing); max-width: 50%; } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-style-align-left { float: left; margin-right: var(--ck-image-style-spacing); } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-style-align-center { margin-left: auto; margin-right: auto; } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-style-align-right { float: right; margin-left: var(--ck-image-style-spacing); } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-style-block-align-right { margin-right: 0; margin-left: auto; } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-style-block-align-left { margin-left: 0; margin-right: auto; } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ p+.image-style-align-left, p+.image-style-align-right, p+.image-style-side { margin-top: 0; } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-inline.image-style-align-left, .image-inline.image-style-align-right { margin-top: var(--ck-inline-image-style-spacing); margin-bottom: var(--ck-inline-image-style-spacing); } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-inline.image-style-align-left { margin-right: var(--ck-inline-image-style-spacing); } /* @ckeditor/ckeditor5-image/theme/imagestyle.css */ .image-inline.image-style-align-right { margin-left: var(--ck-inline-image-style-spacing); } /* @ckeditor/ckeditor5-basic-styles/theme/code.css */ code { background-color: hsla(0, 0%, 78%, 0.3); padding: .15em; border-radius: 2px; } /* @ckeditor/ckeditor5-font/theme/fontsize.css */ .text-tiny { font-size: .7em; } /* @ckeditor/ckeditor5-font/theme/fontsize.css */ .text-small { font-size: .85em; } /* @ckeditor/ckeditor5-font/theme/fontsize.css */ .text-big { font-size: 1.4em; } /* @ckeditor/ckeditor5-font/theme/fontsize.css */ .text-huge { font-size: 1.8em; } /* Add global styles */ dt { font-weight: 700; } {% endif %}

3. 優化部落格圖片載入效能

A. 取代 posts.liquid 的過時語法

步驟一:找到 <div class="List-item-excerpt js-list-item-excerpt">{{ post.excerpt }}</div>

步驟二:取代成以下這段 liquid

<div class="List-item-excerpt js-list-item-excerpt"> {% if post.thumbnail_info != blank %} {{ post.thumbnail_info.url | image_tag: use_lazysizes: true, widths: '200, 400, 600, 800', alt: post.thumbnail_info.alt }} {% endif %} </div>