Compare commits

..

No commits in common. "21df14bc2cc466d380607be8e886f200ef382aeb" and "21475448d20d5cfaa2dc503cd564bcbbe3275cfa" have entirely different histories.

37 changed files with 317 additions and 2142 deletions

View File

@ -1,39 +0,0 @@
# Generated by Django 5.2.7 on 2026-01-09 05:52
import django.db.models.deletion
import wagtail.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0002_socialmediasettings_alter_navigationsettings_options_and_more'),
('wagtailimages', '0027_image_description'),
]
operations = [
migrations.AlterField(
model_name='socialmediasettings',
name='links',
field=wagtail.fields.StreamField([('link', 2)], block_lookup={0: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('facebook', 'Facebook'), ('twitter', 'Twitter'), ('instagram', 'Instagram'), ('threads', 'Threads'), ('linkedin', 'LinkedIn'), ('youtube', 'YouTube')]}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('platform', 0), ('url', 1)]], {})}),
),
migrations.CreateModel(
name='BannerSnippet',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.CharField(blank=True, help_text='識別用 key例如 home / category', max_length=50)),
('title', models.CharField(blank=True, max_length=255)),
('link_url', models.URLField(blank=True)),
('link_text', models.CharField(blank=True, max_length=100)),
('is_active', models.BooleanField(default=True)),
('sort_order', models.PositiveIntegerField(default=0)),
('image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')),
],
options={
'verbose_name': 'Banner',
'verbose_name_plural': 'Banners',
'ordering': ['sort_order', 'id'],
},
),
]

View File

@ -1,29 +0,0 @@
# Generated by Django 5.2.7 on 2026-01-09 09:01
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0003_alter_socialmediasettings_links_bannersnippet'),
('wagtailimages', '0027_image_description'),
]
operations = [
migrations.RemoveField(
model_name='headersettings',
name='logo',
),
migrations.AddField(
model_name='headersettings',
name='logo_dark',
field=models.ForeignKey(blank=True, help_text='淺色底用(深色 logo', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'),
),
migrations.AddField(
model_name='headersettings',
name='logo_light',
field=models.ForeignKey(blank=True, help_text='深色底用(亮色 logo', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'),
),
]

View File

@ -29,21 +29,12 @@ from wagtail import blocks
@register_setting
class HeaderSettings(BaseGenericSetting):
logo_light = models.ForeignKey(
logo = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
help_text="深色底用(亮色 logo",
)
logo_dark = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
help_text="淺色底用(深色 logo",
)
site_name = models.CharField(max_length=255, blank=True)
extra_links = StreamField([
@ -56,8 +47,7 @@ class HeaderSettings(BaseGenericSetting):
panels = [
MultiFieldPanel(
[
FieldPanel("logo_light"),
FieldPanel("logo_dark"),
FieldPanel("logo"),
FieldPanel("site_name"),
FieldPanel("extra_links"),
],
@ -92,7 +82,7 @@ class SocialLinkBlock(blocks.StructBlock):
("facebook", "Facebook"),
("twitter", "Twitter"),
("instagram", "Instagram"),
("threads", "Threads"),
("thread", "Thread"),
("linkedin", "LinkedIn"),
("youtube", "YouTube"),
]
@ -112,40 +102,6 @@ class SocialMediaSettings(BaseGenericSetting):
panels = [FieldPanel("links")]
@register_snippet
class BannerSnippet(models.Model):
key = models.CharField(max_length=50, blank=True, help_text="識別用 key例如 home / category")
title = models.CharField(max_length=255, blank=True)
image = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=False,
on_delete=models.SET_NULL,
related_name="+",
)
link_url = models.URLField(blank=True)
link_text = models.CharField(max_length=100, blank=True)
is_active = models.BooleanField(default=True)
sort_order = models.PositiveIntegerField(default=0)
panels = [
FieldPanel("key"),
FieldPanel("title"),
FieldPanel("image"),
FieldPanel("link_url"),
FieldPanel("link_text"),
FieldPanel("is_active"),
FieldPanel("sort_order"),
]
class Meta:
ordering = ["sort_order", "id"]
verbose_name = "Banner"
verbose_name_plural = "Banners"
def __str__(self):
return self.title or f"Banner {self.pk}"
@register_snippet
class FooterText(
DraftStateMixin,

View File

@ -1,27 +0,0 @@
{% load wagtailimages_tags banner_tags %}
{% get_banners banner_key as banners %}
{% if banners %}
<div class="banner-snippets">
{% for banner in banners %}
<div class="banner-snippets__item">
{% if banner.link_url %}
<a class="banner-snippets__link" href="{{ banner.link_url }}">
{% image banner.image fill-1200x300 alt=banner.title %}
{% if banner.title %}
<span class="banner-snippets__title">{{ banner.title }}</span>
{% endif %}
{% if banner.link_text %}
<span class="banner-snippets__cta">{{ banner.link_text }}</span>
{% endif %}
</a>
{% else %}
{% image banner.image fill-1200x300 alt=banner.title %}
{% if banner.title %}
<span class="banner-snippets__title">{{ banner.title }}</span>
{% endif %}
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}

View File

@ -1,20 +0,0 @@
{% load wagtailimages_tags banner_tags %}
{% get_banners "home" first=True as banner %}
{% if banner %}
<div class="home-banner">
{% if banner.link_url %}
<a class="home-banner__link" href="{{ banner.link_url }}">
{% image banner.image width-1280 alt=banner.title %}
{% if banner.title %}
<span class="home-banner__title">{{ banner.title }}</span>
{% endif %}
{% if banner.link_text %}
<span class="home-banner__cta">{{ banner.link_text }}</span>
{% endif %}
</a>
{% else %}
{% image banner.image width-1280 alt=banner.title %}
{% endif %}
</div>
{% endif %}

View File

@ -1,15 +0,0 @@
from django import template
from base.models import BannerSnippet
register = template.Library()
@register.simple_tag
def get_banners(key=None, first=False):
banners = BannerSnippet.objects.filter(is_active=True, image__isnull=False)
if key:
banners = banners.filter(key=key)
if first:
return banners.first()
return banners

View File

@ -1,18 +0,0 @@
# Generated by Django 5.2.7 on 2026-01-09 05:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0003_categorypage_latestpage_trendingpage_articlepage_and_more'),
]
operations = [
migrations.AddField(
model_name='articlepage',
name='not_news',
field=models.BooleanField(default=False, help_text='不列入最新消息區塊', verbose_name='Not News'),
),
]

View File

@ -18,9 +18,8 @@ def _get_env_int(name, default):
except ValueError:
return default
NEWS_SIZE = _get_env_int("HOMEPAGE_NEWS_SIZE", 8) # Default to 8 articles in news layouts
BLOCK_SIZE = _get_env_int("HOMEPAGE_BLOCK_SIZE", 5) # Default to 5 articles in block layout
HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 4) # Default to 8 articles in horizon layout
HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 8) # Default to 8 articles in horizon layout
PAGE_SIZE = _get_env_int("HOMEPAGE_PAGE_SIZE", 10) # Default to 10 articles per page for pagination
# Mixin for Category-related functionality
@ -37,7 +36,7 @@ class CategoryMixin:
"title": category.title,
"items": ArticlePage.objects.child_of(category)
.live()
.order_by("-date", "-id")[:HORIZON_SIZE],
.order_by("-date")[:HORIZON_SIZE],
"url": category.url,
"layout": "horizon",
}
@ -47,7 +46,7 @@ class CategoryMixin:
paginator = Paginator(
ArticlePage.objects.child_of(self)
.live()
.order_by("-date", "-id"),
.order_by("-date"),
PAGE_SIZE,
)
page_number = request.GET.get("page") if request else None
@ -86,15 +85,15 @@ class CategoryMixin:
# No request means no pagination (e.g., homepage)
return {
"title": latest_page.title,
"items": ArticlePage.objects.filter(not_news=False).live().order_by("-date", "-id")[
:NEWS_SIZE
"items": ArticlePage.objects.live().order_by("-date")[
:BLOCK_SIZE
],
"url": latest_page.url,
}
else:
# Paginated view
paginator = Paginator(
ArticlePage.objects.live().order_by("-date", "-id"), PAGE_SIZE
ArticlePage.objects.live().order_by("-date"), PAGE_SIZE
)
page_number = request.GET.get("page")
@ -113,7 +112,7 @@ class CategoryMixin:
def get_trending_articles(self, request=None, exclude_ids=None):
trending_page = TrendingPage.objects.first()
articles_qs = ArticlePage.objects.filter(trending=True).live().order_by(
"-date", "-id"
"-date"
)
# Exclude specified article IDs
@ -155,24 +154,21 @@ class HomePage(Page, CategoryMixin):
}
latest_section = self.get_latest_articles().copy()
# latest_section["layout"] = "block"
latest_section["layout"] = "block"
sections["top_section"].append(latest_section)
# NOT NEED TRENDING SECTION CURRENTLY
# ------------------------------------------------------------------
# Exclude latest articles from trending section
# latest_items = latest_section.get("items", [])
# if hasattr(latest_items, "values_list"):
# latest_ids = list(latest_items.values_list("id", flat=True))
# else:
# latest_ids = [item.id for item in latest_items]
latest_items = latest_section.get("items", [])
if hasattr(latest_items, "values_list"):
latest_ids = list(latest_items.values_list("id", flat=True))
else:
latest_ids = [item.id for item in latest_items]
# trending_section = self.get_trending_articles(
# exclude_ids=latest_ids
# ).copy()
# trending_section["layout"] = "horizon"
# sections["top_section"].append(trending_section)
# ------------------------------------------------------------------
trending_section = self.get_trending_articles(
exclude_ids=latest_ids
).copy()
trending_section["layout"] = "horizon"
sections["top_section"].append(trending_section)
# Build category sections
categories = CategoryPage.objects.child_of(self).live().in_menu()
@ -183,7 +179,7 @@ class HomePage(Page, CategoryMixin):
"url": category.url,
"items": ArticlePage.objects.descendant_of(category)
.live()
.order_by("-date", "-id")[:HORIZON_SIZE],
.order_by("-date")[:HORIZON_SIZE],
"layout": "horizon",
}
)
@ -275,7 +271,6 @@ class ArticlePage(Page):
use_json_field=True,
)
trending = models.BooleanField("Trending", default=False, help_text="在熱門區塊顯示")
not_news = models.BooleanField("Not News", default=False, help_text="不列入最新消息區塊")
tags = ClusterTaggableManager(through="home.ArticlePageTag", blank=True)
search_fields = Page.search_fields + [
@ -286,7 +281,6 @@ class ArticlePage(Page):
content_panels = Page.content_panels + [
FieldPanel("trending"),
FieldPanel("not_news"),
FieldPanel("cover_image"),
FieldPanel("banner_image"),
FieldPanel("date"),
@ -305,7 +299,7 @@ class ArticlePage(Page):
.exclude(id=self.id)
.filter(tags__id__in=tag_ids)
.distinct()
.order_by("-date", "-id")[:4]
.order_by("-date")[:4]
)
else:
related_articles = ArticlePage.objects.none()

View File

@ -1,79 +0,0 @@
.block-list-wrap {
position: relative;
}
.block-list-arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 36px;
height: 36px;
border: 0;
border-radius: 8px;
background: rgba(255, 255, 255, 0.85);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.18);
cursor: pointer;
}
.block-list-arrow[data-dir="left"] {
left: 0;
}
.block-list-arrow[data-dir="right"] {
right: 0;
}
.block-list-arrow.is-hidden {
opacity: 0;
pointer-events: none;
}
.block-list {
display: flex;
align-items: stretch;
gap: 20px;
overflow-x: auto;
scroll-snap-type: x mandatory;
list-style: none;
margin: 24px 0;
padding: 12px 48px;
}
.block-list li {
flex: 0 0 200px;
overflow: hidden;
scroll-snap-align: start;
}
.block-list li a {
display: flex;
flex-direction: column;
gap: 12px;
color: inherit;
text-decoration: none;
padding: 16px;
}
.block-list li img {
width: 100%;
height: 200px;
/* object-fit: contain; */
display: block;
/* background: #f5f5f5; */
/* border-radius: 8px; */
}
.block-list li a:hover,
.block-list li a:focus {
transform: translateY(-2px);
}
.block-list .empty {
flex: 1 0 auto;
text-align: center;
padding: 24px;
color: #666;
background: #fafafa;
border: 1px dashed #ddd;
border-radius: 12px;
}

View File

@ -1,130 +0,0 @@
.home-hero-band {
background-color: #0e1b42;
color: #ffffff;
padding-bottom: 86px;
}
.home-hero-band a {
color: #ffffff;
}
.template-homepage .site-header {
background-color: #0e1b42;
color: #ffffff;
}
.template-homepage .main-menu-link {
color: #ffffff;
}
.template-homepage .header-search .search-input {
/* border-color: #ffffff; */
background-color: rgba(255, 255, 255, 0.4);
}
.template-homepage .header-search .search-icon {
color: #ffffff;
}
.template-homepage .header-search input[type="search"] {
color: #ffffff;
}
.home-banner img {
width: 100%;
height: auto;
display: block;
}
@media (max-width: 767px) {
.home-banner {
width: 100vw;
margin-left: calc(50% - 50vw);
}
}
.list-title {
align-items: center;
margin: 10px 0;
}
.block-title {
display: inline-block;
width: 197px;
height: 87px;
vertical-align: middle;
font-size: 20px;
font-weight: 700;
}
.block-title span {
padding-left: 21px;
line-height: 87px;
}
.block-title-divider {
display: inline-flex;
width: 28px;
height: 1px;
transform: translate(-4px, -4px);
}
.more-link {
text-decoration: none;
font-size: 16px;
}
.section-b .list-title {
color: #00abf5;
}
.section-b .list-title .category_title {
background-color: #00abf5;
color: #ffffff;
}
.section-b .list-title a{
color: #00abf5;
}
.section-o .list-title {
color: #f4a41c;
}
.section-o .list-title .category_title {
background-color: #f4a41c;
color: #ffffff;
}
.section-o .list-title a{
color: #f4a41c;
}
@media (min-width: 768px) and (max-width: 1023px) {
.list-title .category_title {
width: 142px;
}
}
@media (max-width: 574px) {
.block-title {
width: 139px;
height: 55px;
font-size: 16px;
}
.block-title span {
padding-left: 14px;
line-height: 55px;
}
.block-title-divider {
width: 20px;
height: 1px;
transform: translate(-4px, -4px);
}
.more-link {
font-size: 14px;
}
}

View File

@ -1,236 +0,0 @@
.horizontal-list-wrap {
position: relative;
}
.horizontal-list-arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
width: 52px;
height: 52px;
border: 0;
border-radius: 8px;
background: rgba(255, 255, 255, 0.85);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.18);
cursor: pointer;
}
.horizontal-list-arrow[data-dir="left"] {
left: 0;
}
.horizontal-list-arrow[data-dir="right"] {
right: 0;
}
.horizontal-list-arrow-icon {
width: 18px;
height: 34px;
display: block;
}
.horizontal-list-arrow[data-dir="right"] .horizontal-list-arrow-icon {
transform: rotate(180deg);
}
.horizontal-list-arrow.is-hidden {
opacity: 0;
pointer-events: none;
}
.horizontal-list {
display: flex;
align-items: stretch;
gap: 30px;
overflow-x: auto;
scroll-snap-type: x mandatory;
list-style: none;
margin: 24px 0;
padding: 0;
scrollbar-width: none;
-ms-overflow-style: none;
}
.horizontal-list::-webkit-scrollbar {
display: none;
}
.horizontal-list li {
flex: 0 0 200px;
overflow: hidden;
scroll-snap-align: start;
}
.horizontal-list li a {
display: flex;
flex-direction: column;
gap: 12px;
color: inherit;
text-decoration: none;
}
.horizontal-list-thumb {
position: relative;
width: 194px;
height: 133px;
border-radius: 10px;
overflow: hidden;
}
.horizontal-list-thumb::after {
content: "";
position: absolute;
left: 0px;
right: 0px;
bottom: 0px;
height: 25px;
pointer-events: none;
}
.section-b .horizontal-list-thumb::after {
background: url("../img/picfrm_b194.png") no-repeat left bottom / cover;
background-size: 194px 25px;
}
.section-o .horizontal-list-thumb::after {
background: url("../img/picfrm_o194.png") no-repeat left bottom / cover;
background-size: 194px 25px;
}
.horizontal-list li img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.horizontal-list li a:hover,
.horizontal-list li a:focus {
transform: translateY(-2px);
}
.horizontal-list li a span {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.horizontal-list .article-title {
font-size: 20px;
}
.horizontal-list .article-intro {
font-size: 16px;
}
.horizontal-list .article-date {
color: #0e1b4266;
font-size: 16px;
}
.horizontal-list .empty {
flex: 1 0 auto;
text-align: center;
padding: 24px;
color: #666;
background: #fafafa;
border: 1px dashed #ddd;
border-radius: 12px;
}
@media (min-width: 768px) and (max-width: 1023px), (max-width: 574px) {
.horizontal-list {
gap: 16px;
}
.horizontal-list-thumb {
width: 139px;
height: 114px;
}
.section-b .horizontal-list-thumb::after {
background: url("../img/picfrm_b139.png") no-repeat left bottom / cover;
background-size: 139px 25px;
}
.section-o .horizontal-list-thumb::after {
background: url("../img/picfrm_o139.png") no-repeat left bottom / cover;
background-size: 139px 25px;
}
.horizontal-list a > div:nth-of-type(3) {
display: none;
}
}
@media (min-width: 768px) and (max-width: 1023px){
.horizontal-list li {
flex: 0 0 145px;
}
.horizontal-list .article-title {
width: 145px;
}
}
@media (min-width: 575px) and (max-width: 767px) {
.horizontal-list {
flex-direction: column;
gap: 16px;
overflow-x: visible;
}
.horizontal-list li {
flex: 0 0 auto;
}
.horizontal-list li a {
display: grid;
grid-template-columns: 200px 1fr;
column-gap: 16px;
row-gap: 6px;
}
.horizontal-list li a > div:nth-of-type(1) {
grid-row: 1 / span 3;
}
.horizontal-list li a > div:nth-of-type(2),
.horizontal-list li a > div:nth-of-type(3),
.horizontal-list li a > div:nth-of-type(4) {
grid-column: 2;
}
}
@media (max-width: 574px) {
.horizontal-list-arrow {
display: none;
}
.horizontal-list {
flex-wrap: wrap;
overflow-x: visible;
scroll-snap-type: none;
}
.horizontal-list li {
flex: 0 0 142px;
}
/* .horizontal-list .article-title {
width: 142px;
} */
.horizontal-list .article-title {
font-size: 16px;
}
.horizontal-list .article-date {
font-size: 12px;
}
}

View File

@ -1,424 +0,0 @@
.news-title, .more-news-title {
background-color: #ffffff;
color: #0e1b42;
}
.news-hero {
display: grid;
grid-template-columns: 480px 1fr;
grid-template-areas:
"header header"
"image content";
gap: 16px 40px;
align-items: start;
}
.news-hero .news-hero-header {
grid-area: header;
display: flex;
align-items: center;
gap: 12px;
}
.news-hero .list-title {
grid-area: title;
max-width: 310px;
flex: 0 1 310px;
display: block;
}
.news-hero .fist-news-title {
grid-area: head;
max-width: 580px;
font-size: 40px;
font-weight: 400;
color: #eb9f13;
margin: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.news-hero .first-news-image {
grid-area: image;
position: relative;
border-radius: 10px;
overflow: hidden;
width: 480px;
}
.news-hero .first-news-image::after {
content: "";
position: absolute;
left: 0px;
right: 0px;
bottom: 0px;
height: 25px;
background: url("../img/picfrm_b480.png") no-repeat left bottom / cover;
background-size: 480px 25px;
pointer-events: none;
}
.news-hero .first-news-image img {
display: block;
width:480px;
height:293px;
object-fit:cover;
border-radius: 10px;
}
.news-hero .first-news-content {
grid-area: content;
display: flex;
flex-direction: column;
gap: 16px;
max-height: 293px;
}
.news-hero .fist-news-date,
.news-hero .first-news-intro {
flex: 0 0 auto;
}
.news-hero .fist-news-date {
color: rgba(255, 255, 255, 0.4);
}
.news-hero .first-news-intro {
font-size: 20px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.news-hero .first-news-body {
font-size: 13px;
flex: 1 1 auto;
overflow: hidden;
position: relative;
}
.news-hero .first-news-body::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 100px;
background: linear-gradient(to bottom, rgba(14, 27, 66, 0), #0e1b42);
pointer-events: none;
}
.news-list-items {
display: contents;
}
.news-list-lower {
margin-top: 24px;
display: flex;
flex-wrap: wrap;
gap: 28px;
}
.news-list-items a {
display: flex;
flex-direction: column;
gap: 12px;
width: 200px;
text-decoration: none;
}
.news-list-thumb {
position: relative;
width: 194px;
height: 133px;
border-radius: 10px;
overflow: hidden;
}
.news-list-thumb::after {
content: "";
position: absolute;
left: 0px;
right: 0px;
bottom: 0px;
height: 25px;
background: url("../img/picfrm_o194.png") no-repeat left bottom / cover;
background-size: 194px 25px;
pointer-events: none;
}
.news-list-thumb img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.news-list-items a span{
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.news-list-items .article-title {
font-size: 20px;
}
.news-list-items .article-date {
color: rgba(255, 255, 255, 0.4);
}
.more-news {
flex: 1 1 200px;
display: flex;
flex-direction: column;
gap: 16px;
}
.more-news .article-title {
max-width: 200px;
font-size: 20px;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
@media (min-width: 575px) and (max-width: 1023px) {
.more-news {
display: grid;
grid-template-columns: 200px 1fr 1fr;
gap: 8px 16px;
align-items: start;
}
.more-news .more-news-title {
grid-column: 1;
grid-row: 1 / span 2;
align-self: stretch;
}
.more-news .article-title {
max-width: none;
}
.more-news a:nth-of-type(1) {
grid-column: 2;
grid-row: 1;
}
.more-news a:nth-of-type(2) {
grid-column: 2;
grid-row: 2;
}
.more-news a:nth-of-type(3) {
grid-column: 3;
grid-row: 1;
}
.more-news a:nth-of-type(4) {
grid-column: 3;
grid-row: 2;
}
}
@media (max-width: 767px) {
.news-hero {
grid-template-columns: 1fr;
grid-template-areas:
"title"
"image"
"head"
"content";
}
.news-hero .news-hero-header {
display: contents;
}
.news-hero .list-title {
max-width: none;
}
}
@media (min-width: 575px) and (max-width: 767px) {
.news-list-items a {
display: grid;
grid-template-columns: 194px 1fr;
column-gap: 16px;
row-gap: 8px;
width: 100%;
}
.news-hero .first-news-image {
width: 426px;
}
.news-hero .first-news-image::after {
background: url("../img/picfrm_b426.png") no-repeat left bottom / cover;
background-size: 426px 25px;
}
.news-hero .first-news-image img {
width:426px;
height:260px;
}
.news-list-items a > div:nth-of-type(1) {
grid-row: 1 / span 3;
}
.news-list-items a > div:nth-of-type(2),
.news-list-items a > div:nth-of-type(3),
.news-list-items a > div:nth-of-type(4) {
grid-column: 2;
}
.more-news {
grid-template-columns: 1fr 1fr;
}
.more-news .more-news-title {
margin-bottom: 20px;
grid-column: 1 / -1;
grid-row: 1;
}
.more-news .article-title {
margin-top: 10px;
}
.more-news a:nth-of-type(1) {
grid-column: 1;
grid-row: 2;
}
.more-news a:nth-of-type(2) {
grid-column: 2;
grid-row: 2;
}
.more-news a:nth-of-type(3) {
grid-column: 1;
grid-row: 3;
}
.more-news a:nth-of-type(4) {
grid-column: 2;
grid-row: 3;
}
}
@media (min-width: 768px) and (max-width: 1023px) {
.news-hero {
grid-template-columns: 318px 1fr;
}
.news-hero .fist-news-title {
max-width: 320px;
}
.news-hero .first-news-image {
width: 318px;
}
.news-hero .first-news-image::after {
background-size: 318px 25px;
}
.news-hero .first-news-image img {
width: 318px;
height: 290px;
}
.news-hero .first-news-content {
height: 290px;
}
.news-list-lower {
gap: 16px;
}
}
@media (min-width: 575px) and (max-width: 1023px) {
.news-hero .fist-news-title {
font-size: 36px;
}
}
@media (max-width: 574px) {
.news-hero .first-news-image {
width: 300px;
height: 220px;
justify-self: center;
}
.news-hero .first-news-image::after {
background: url("../img/picfrm_b300.png") no-repeat left bottom / cover;
background-size: 300px 25px;
}
.news-hero .first-news-image img {
width: 300px;
height: 220px;
}
/* .news-hero .first-news-content {
height: 220px;
} */
.news-hero .fist-news-title {
font-size: 24px;
}
.news-hero .first-news-intro {
font-size: 16px;
}
.news-list-lower {
gap: 16px;
}
.news-list-items a {
width: 142px;
}
/* .news-list-lower {
justify-content: center;
} */
.news-list-thumb {
width: 139px;
height: 110px;
}
.news-list-thumb::after {
background: url("../img/picfrm_o139.png") no-repeat left bottom / cover;
background-size: 139px 25px;
}
.news-list-items a > div:nth-of-type(3) {
display: none;
}
.news-list-items .article-title {
font-size: 16px;
}
.news-list-items .article-date {
font-size: 12px;
}
.more-news{
max-width: 142px;
gap: 12px;
}
.more-news .article-title {
font-size: 16px;
}
}

View File

@ -0,0 +1,184 @@
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
max-width: 960px;
min-height: 100vh;
margin: 0 auto;
padding: 0 15px;
color: #231f20;
font-family: 'Helvetica Neue', 'Segoe UI', Arial, sans-serif;
line-height: 1.25;
}
a {
background-color: transparent;
color: #308282;
text-decoration: underline;
}
a:hover {
color: #ea1b10;
}
h1,
h2,
h3,
h4,
h5,
p,
ul {
padding: 0;
margin: 0;
font-weight: 400;
}
svg:not(:root) {
overflow: hidden;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #e6e6e6;
}
.logo {
width: 150px;
margin-inline-end: 20px;
}
.logo a {
display: block;
}
.figure-logo {
max-width: 150px;
max-height: 55.1px;
}
.release-notes {
font-size: 14px;
}
.main {
padding: 40px 0;
margin: 0 auto;
text-align: center;
}
.figure-space {
max-width: 265px;
}
@keyframes pos {
0%, 100% {
transform: rotate(-6deg);
}
50% {
transform: rotate(6deg);
}
}
.egg {
fill: #43b1b0;
animation: pos 3s ease infinite;
transform: translateY(50px);
transform-origin: 50% 80%;
}
.main-text {
max-width: 400px;
margin: 5px auto;
}
.main-text h1 {
font-size: 22px;
}
.main-text p {
margin: 15px auto 0;
}
.footer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
border-top: 1px solid #e6e6e6;
padding: 10px;
}
.option {
display: block;
padding: 10px 10px 10px 34px;
position: relative;
text-decoration: none;
}
.option svg {
width: 24px;
height: 24px;
fill: gray;
border: 1px solid #d9d9d9;
padding: 5px;
border-radius: 100%;
top: 10px;
inset-inline-start: 0;
position: absolute;
}
.option h2 {
font-size: 19px;
text-decoration: underline;
}
.option p {
padding-top: 3px;
color: #231f20;
font-size: 15px;
font-weight: 300;
}
@media (max-width: 996px) {
body {
max-width: 780px;
}
}
@media (max-width: 767px) {
.option {
flex: 0 0 50%;
}
}
@media (max-width: 599px) {
.main {
padding: 20px 0;
}
.figure-space {
max-width: 200px;
}
.footer {
display: block;
width: 300px;
margin: 0 auto;
}
}
@media (max-width: 360px) {
.header-link {
max-width: 100px;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,45 +0,0 @@
(function () {
function initBlockList(block) {
var list = block.querySelector('.block-list');
var left = block.querySelector('[data-dir="left"]');
var right = block.querySelector('[data-dir="right"]');
if (!list || !left || !right) {
return;
}
function getScrollAmount() {
return Math.max(200, Math.floor(list.clientWidth * 0.8));
}
function updateArrows() {
var maxScroll = list.scrollWidth - list.clientWidth;
var hasOverflow = maxScroll > 1;
var atStart = list.scrollLeft <= 1;
var atEnd = list.scrollLeft >= maxScroll - 1;
left.classList.toggle('is-hidden', !hasOverflow || atStart);
right.classList.toggle('is-hidden', !hasOverflow || atEnd);
}
left.addEventListener('click', function () {
list.scrollBy({ left: -getScrollAmount(), behavior: 'smooth' });
});
right.addEventListener('click', function () {
list.scrollBy({ left: getScrollAmount(), behavior: 'smooth' });
});
list.addEventListener('scroll', function () {
window.requestAnimationFrame(updateArrows);
});
window.addEventListener('resize', updateArrows);
updateArrows();
}
document.addEventListener('DOMContentLoaded', function () {
var blocks = document.querySelectorAll('[data-block-list]');
blocks.forEach(initBlockList);
});
})();

View File

@ -1,45 +0,0 @@
(function () {
function initHorizontalList(block) {
var list = block.querySelector('.horizontal-list');
var left = block.querySelector('[data-dir="left"]');
var right = block.querySelector('[data-dir="right"]');
if (!list || !left || !right) {
return;
}
function getScrollAmount() {
return Math.max(200, Math.floor(list.clientWidth * 0.8));
}
function updateArrows() {
var maxScroll = list.scrollWidth - list.clientWidth;
var hasOverflow = maxScroll > 1;
var atStart = list.scrollLeft <= 1;
var atEnd = list.scrollLeft >= maxScroll - 1;
left.classList.toggle('is-hidden', !hasOverflow || atStart);
right.classList.toggle('is-hidden', !hasOverflow || atEnd);
}
left.addEventListener('click', function () {
list.scrollBy({ left: -getScrollAmount(), behavior: 'smooth' });
});
right.addEventListener('click', function () {
list.scrollBy({ left: getScrollAmount(), behavior: 'smooth' });
});
list.addEventListener('scroll', function () {
window.requestAnimationFrame(updateArrows);
});
window.addEventListener('resize', updateArrows);
updateArrows();
}
document.addEventListener('DOMContentLoaded', function () {
var blocks = document.querySelectorAll('[data-horizontal-list]');
blocks.forEach(initHorizontalList);
});
})();

View File

@ -1,32 +1,20 @@
{% extends "base.html" %}
{% load static %}
{% block body_class %}template-homepage{% endblock %}
{% block extra_css %}
<link rel="stylesheet" type="text/css" href="{% static 'css/home.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/news_list.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/block_list.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/horizontal_list.css' %}">
{% endblock %}
{% block content %}
<div class="home-hero-band full-bleed">
<div class="site-container">
<div class="home-hero">
{% include "base/includes/home_banner.html" %}
{% with top_section=sections.top_section %}
{% for section in top_section %}
{% include "home/includes/news_list.html" with section=section %}
{% endfor %}
{% endwith %}
</div>
</div>
</div>
{% with top_section=sections.top_section %}
<h2>
<a href="{{ top_section.0.url }}">最新文章</a>
</h2>
{% for section in top_section %}
{% if section.layout == "block" %}
{% include "home/includes/block_list.html" with items=section.items %}
{% elif section.layout == "horizon" %}
{% include "home/includes/horizontal_list.html" with items=section.items %}
{% endif %}
{% endfor %}
{% endwith %}
{% for section in sections.category_sections %}
{% cycle 'section-b' 'section-o' as section_color silent %}
{% include "home/includes/category_session.html" with section=section section_color=section_color %}
{% include "home/includes/category_session.html" with section=section %}
{% endfor %}
{% endblock content %}
{% block extra_js %}
<script type="text/javascript" src="{% static 'js/block_list.js' %}"></script>
<script type="text/javascript" src="{% static 'js/horizontal_list.js' %}"></script>
{% endblock %}

View File

@ -1,23 +1,19 @@
{% load wagtailimages_tags static %}
<div class="block-list-wrap" data-block-list>
<button class="block-list-arrow" type="button" data-dir="left" aria-label="上一頁"></button>
<ul class="block-list">
{% for article in items %}
<li>
<a href="{{ article.url }}">
{% if article.cover_image %}
{% image article.cover_image max-200x200 as cover %}
<img src="{{ cover.url }}" alt="{{ article.title }}" height="200" width="200" style="width:200px;height:200px;object-fit:contain;display:block;"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ article.title }}" height="200" width="200" style="width:200px;height:200px;object-fit:contain;display:block;"/>
{% endif %}
{{ article.title }}
</a>
</li>
{% empty %}
<li class="empty">目前沒有文章</li>
{% endfor %}
</ul>
<button class="block-list-arrow" type="button" data-dir="right" aria-label="下一頁"></button>
</div>
<ul class="block-list">
{% for article in items %}
<li>
<a href="{{ article.url }}">
{% if article.cover_image %}
{% image article.cover_image max-200x200 as cover %}
<img src="{{ cover.url }}" alt="{{ article.title }}" height="200" width="200" style="width:200px;height:200px;object-fit:contain;display:block;"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ article.title }}" height="200" width="200" style="width:200px;height:200px;object-fit:contain;display:block;"/>
{% endif %}
{{ article.title }}
</a>
</li>
{% empty %}
<li class="empty">目前沒有文章</li>
{% endfor %}
</ul>

View File

@ -1,15 +1,13 @@
{% load wagtailimages_tags static %}
<section class="article-section article-section--{{ section.layout }} {{ section_color }}">
<div class="list-title {{ title_variant }}">
<div class="block-title category_title"><span>{{ section.title }}</span></div>
<span class="block-title-divider" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" height="100%" overflow="visible" preserveAspectRatio="none" viewBox="0 0 28 1" width="100%">
<line stroke="currentColor" x2="28" y1="0.5" y2="0.5"/>
</svg>
</span>
<a href="{{ section.url }}"><span class="more-link">查看全部</span></a>
</div>
<section class="article-section article-section--{{ section.layout }}">
<h2>
{% if section.url %}
<a href="{{ section.url }}">{{ section.title }}</a>
{% else %}
{{ section.title }}
{% endif %}
</h2>
{% if section.layout == "block" %}
{% include "home/includes/block_list.html" with items=section.items %}
{% elif section.layout == "horizon" %}

View File

@ -1,35 +1,19 @@
{% load wagtailimages_tags static %}
<div class="horizontal-list-wrap" data-horizontal-list>
<button class="horizontal-list-arrow is-hidden" type="button" data-dir="left" aria-label="上一頁">
<svg class="horizontal-list-arrow-icon" xmlns="http://www.w3.org/2000/svg" fill="none" overflow="visible" preserveAspectRatio="none" viewBox="0 0 18.1213 33.4142">
<path d="M17.4142 0.707107L1.41421 16.7071L17.4142 32.7071" id="Vector 3" stroke="var(--stroke-0, #0E1B42)" stroke-width="2"/>
</svg>
</button>
<ul class="horizontal-list">
{% for article in items %}
<li>
<a href="{{ article.url }}">
<div class="horizontal-list-thumb">
{% if article.cover_image %}
{% image article.cover_image max-194x133 as cover %}
<img src="{{ cover.url }}" alt="{{ article.title }}" height="133" width="194"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ article.title }}" height="133" width="194"/>
{% endif %}
</div>
<div><span class="article-title">{{ article.title }}</span></div>
<div><span class="article-intro">{{ article.intro }}</span></div>
<div><span class="article-date">{{ article.date|date:"Y.m.d" }}</span></div>
</a>
</li>
{% empty %}
<li class="empty">目前沒有文章</li>
{% endfor %}
</ul>
<button class="horizontal-list-arrow" type="button" data-dir="right" aria-label="下一頁">
<svg class="horizontal-list-arrow-icon" xmlns="http://www.w3.org/2000/svg" fill="none" overflow="visible" preserveAspectRatio="none" viewBox="0 0 18.1213 33.4142">
<path d="M17.4142 0.707107L1.41421 16.7071L17.4142 32.7071" id="Vector 3" stroke="var(--stroke-0, #0E1B42)" stroke-width="2"/>
</svg>
</button>
</div>
<ul class="horizontal-list">
{% for article in items %}
<li>
<a href="{{ article.url }}">
{% if article.cover_image %}
{% image article.cover_image max-200x200 as cover %}
<img src="{{ cover.url }}" alt="{{ article.title }}" height="200" width="200" style="width:200px;height:200px;object-fit:cover;display:block;"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ article.title }}" height="200" width="200" style="width:200px;height:200px;object-fit:cover;display:block;"/>
{% endif %}
<span>{{ article.title }}</span>
</a>
</li>
{% empty %}
<li class="empty">目前沒有文章</li>
{% endfor %}
</ul>

View File

@ -1,84 +0,0 @@
{% load wagtailimages_tags static %}
<div class="news-list-wrap" data-news-list>
<div class="news-hero">
<div class="news-hero-header">
<div class="list-title">
<div class="block-title news-title"><span>本日頭條</span></div>
<span class="block-title-divider" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" height="100%" overflow="visible" preserveAspectRatio="none" viewBox="0 0 28 1" width="100%">
<line stroke="currentColor" x2="28" y1="0.5" y2="0.5"/>
</svg>
</span>
<a href="{{ section.url }}"><span class="more-link">查看全部</span></a>
</div>
{% with first_article=section.items|first %}
{% if first_article %}
<div class="fist-news-title">
<span>{{ first_article.title }}</span>
</div>
{% endif %}
{% endwith %}
</div>
{% with first_article=section.items|first %}
{% if first_article %}
<div class="first-news-image">
<a href="{{ first_article.url }}">
{% if first_article.cover_image %}
{% image first_article.cover_image max-480x320 as cover %}
<img src="{{ cover.url }}" alt="{{ first_article.title }}" height="293" width="480"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ first_article.title }}" height="293" width="480"/>
{% endif %}
</a>
</div>
<div class="first-news-content">
<div class="fist-news-date">
<span>{{ first_article.date|date:"Y.m.d" }}</span>
</div>
{% if first_article.intro %}
<div class="first-news-intro">
<span>{{ first_article.intro }}</span>
</div>
{% endif %}
<div class="first-news-body">
<span>{{ first_article.body_search_text|truncatechars:320 }}</span>
</div>
</div>
{% else %}
<span class="empty">目前沒有文章</span>
{% endif %}
{% endwith %}
</div>
{% if section.items|length >= 2 %}
<div class="news-list-lower">
<div class="news-list-items">
{% for article in section.items|slice:"1:4" %}
<a href="{{ article.url }}">
<div class="news-list-thumb">
{% if article.cover_image %}
{% image article.cover_image max-194x133 as cover %}
<img src="{{ cover.url }}" alt="{{ article.title }}" height="133" width="194"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ article.title }}" height="133" width="194"/>
{% endif %}
</div>
<div><span class="article-title">{{ article.title }}</span></div>
<div><span class="article-intro">{{ article.intro }}</span></div>
<div><span class="article-date">{{ article.date|date:"Y.m.d" }}</span></div>
</a>
{% endfor %}
</div>
<div class="more-news">
<div class="block-title more-news-title"><span>更多頭條</span></div>
{% for article in section.items|slice:"4:8" %}
<a href="{{ article.url }}">
<span class="article-title">{{ article.title }}</span>
</a>
{% endfor %}
</div>
</div>
{% endif %}
</div>

View File

@ -1,579 +0,0 @@
a {
text-decoration: none;
}
.site-container {
max-width: 890px;
margin: 0 auto;
padding: 0 16px;
}
.full-bleed {
width: 100vw;
margin-left: calc(50% - 50vw);
}
.site-header {
position: relative;
z-index: 10;
}
.header-inner {
display: flex;
align-items: center;
gap: 24px;
padding: 30px 0;
}
.logo {
display: inline-flex;
align-items: center;
}
.logo--light {
display: none;
}
.logo--dark {
display: inline-flex;
}
.template-homepage .logo--light {
display: inline-flex;
}
.template-homepage .logo--dark {
display: none;
}
.main-nav {
flex: 1 1 auto;
margin-left: 16px;
top: 56px;
}
.main-menu {
display: flex;
align-items: center;
gap: 24px;
list-style: none;
margin: 0;
padding: 0;
width: 100%;
}
.menu-item {
position: relative;
flex: 1 1 0;
text-align: left;
}
.menu-item-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.submenu-toggle {
display: none;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border: 0;
background: none;
padding: 0;
cursor: pointer;
color: inherit;
}
.submenu-toggle::after {
content: "";
width: 8px;
height: 8px;
border-right: 2px solid currentColor;
border-bottom: 2px solid currentColor;
transform: rotate(45deg);
display: block;
transition: transform 160ms ease;
}
.menu-item.is-open .submenu-toggle::after {
transform: rotate(225deg);
}
.menu-divider {
display: none;
height: 1px;
background: currentColor;
opacity: 0.6;
margin: 8px 0 0;
}
.menu-toggle {
display: none;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 4px;
width: 32px;
height: 32px;
background: none;
border: 0;
padding: 0;
cursor: pointer;
}
.menu-toggle-bar {
width: 20px;
height: 2px;
background: currentColor;
display: block;
}
.menu-toggle.is-open .menu-toggle-bar:nth-child(1) {
transform: translateY(6px) rotate(45deg);
}
.menu-toggle.is-open .menu-toggle-bar:nth-child(2) {
opacity: 0;
}
.menu-toggle.is-open .menu-toggle-bar:nth-child(3) {
transform: translateY(-6px) rotate(-45deg);
}
.main-menu-link {
display: inline-block;
padding: 12px 4px;
font-variation-settings: normal;
color: #0e1b42;
font-family: "Inter:Regular", "Noto Sans JP:Regular", sans-serif;
word-break: break-word;
font-weight: 400;
font-style: normal;
font-size: 14px;
letter-spacing: 0px;
line-height: normal;
--letter-spacing: 0px;
}
.submenu {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
margin-top: -2px;
margin-left: 20px;
/* min-width: 220px; */
list-style: none;
padding-inline-start: 0;
/* padding: 14px 0; */
/* background: #fff; */
/* border-radius: 16px; */
/* box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18); */
border-bottom: #ffffff;
border-style: solid;
border-width: 0 0 1px 0;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 160ms ease, transform 160ms ease;
}
.menu-item:hover .submenu,
.menu-item:focus-within .submenu {
opacity: 1;
visibility: visible;
pointer-events: auto;
transform: translateX(-50%) translateY(2px);
}
.submenu-item {
min-width: 94px;
height: 36px;
/* padding: 8px 16px; */
border: #ffffff;
border-style: solid;
border-width: 1px 1px 0 1px;
background-color: #0e1b42;
/* line-height: 36px; */
/* vertical-align: middle; */
/* align-items: center; */
}
.submenu-item a {
display: block;
font-variation-settings: normal;
color: #ffffff;
font-family: "Inter:Regular", "Noto Sans JP:Regular", sans-serif;
word-break: break-word;
font-weight: 400;
font-style: normal;
font-size: 14px;
letter-spacing: 0px;
line-height: normal;
--letter-spacing: 0px;
padding: 8px 16px;
}
.submenu-item a:hover,
.submenu-item a:focus {
background: rgba(0, 0, 0, 0.05);
}
.header-search .search-input {
display: flex;
align-items: center;
/* border-style: solid; */
/* border-color: #0e1b42; */
/* border-width: 1px; */
}
.header-search {
margin-left: auto;
}
.header-search .search-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
background: none;
border: 0;
padding: 0;
cursor: pointer;
color: #1b2140; /* icon 顏色 */
}
.header-search .search-icon svg {
width: 28px;
height: 28px;
}
.header-search input[type="search"] {
border: 0;
background: transparent;
outline: none;
/* color: rgba(0, 0, 0, 0.3); */
width: 181px; /* 依需要調 */
}
@media (max-width: 1023px) {
.site-container {
max-width: 640px;
}
.header-search input[type="search"] {
width: 90px;
}
}
@media (min-width: 575px) and (max-width: 767px) {
.site-container {
max-width: 426px;
}
.site-header .site-container {
padding: 0 44px;
margin: 0;
}
.main-menu {
justify-content: flex-end;
}
.header-search input[type="search"] {
width: 163px;
}
.main-nav {
right: -28px;
}
}
@media (max-width: 767px) {
.site-header .site-container {
position: relative;
}
.header-inner {
width: 100%;
}
.main-nav {
position: absolute;
/* top: 100%; */
width: 220px;
display: none;
padding: 16px 0;
background: #0e1b42;
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12);
z-index: 5;
}
.main-nav.is-open {
display: block;
}
.main-menu {
flex-direction: column;
align-items: flex-start;
gap: 12px;
width: 220px;
padding: 0 16px;
}
.menu-item {
flex: 0 0 auto;
width: 100%;
text-align: left;
}
.menu-item-header {
width: 192px;
}
.menu-toggle {
display: flex;
margin-left: 12px;
color: #ffffff;
}
.template-homepage .menu-toggle {
color: #ffffff;
}
.template-homepage .main-nav {
background: #0e1b42;
}
.main-menu-link {
color: #ffffff;
font-size: 14px;
}
.submenu-toggle {
display: inline-flex;
color: #ffffff;
}
.menu-divider {
margin-left: 0;
margin-right: 0;
width: 92px;
}
.menu-item.is-open .menu-divider {
display: block;
}
.submenu {
position: static;
/* transform: none; */
margin: 8px 0 0;
opacity: 1;
visibility: visible;
pointer-events: auto;
display: none;
border: 0;
}
/* .menu-item:focus-within .submenu {
opacity: 1;
visibility: visible;
pointer-events: auto;
transform: translateX(-50%) translateY(2px);
} */
.menu-item.is-open .submenu {
display: block;
transform: none;
}
.submenu-item {
background: none;
border: 0;
height: auto;
}
.submenu-item a {
padding: 6px 0 6px 18px;
color: #ffffff;
font-size: 14px;
}
}
@media (max-width: 574px) {
.site-container {
max-width: 300px;
}
/* .header-inner {
padding: 16px 0;
} */
.header-inner {
flex-wrap: wrap;
}
.header-search {
width: 100%;
order: 3;
margin-left: 0;
margin-top: 12px;
}
.header-search input[type="search"] {
width: 195px;
}
.main-nav {
right: 16px;
}
}
@layer figreset {
:root {
font-family: var( --default-font-family,ui-sans-serif,system-ui,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji' )
}
html {
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: transparent;
-webkit-font-smoothing: antialiased;
width: 100%
}
body {
margin: 0;
width: 100%
}
body: has([data-page-overflowx='hidden']) {
overflow-x:hidden
}
body: has([data-page-overflowx='auto']) {
overflow-x:auto
}
}
footer {
background: #0e1b42;
color: #ffffff;
padding: 24px 0;
font-size: 14px;
}
.footer-inner {
display: flex;
justify-content: center;
gap: 48px;
align-items: flex-start;
text-align: left;
}
footer .company-info {
max-width: 300px;
margin: 0;
padding: 0 16px;
}
footer .copyright img {
margin: 10px 0;
width: 265px;
height: 37px;
}
footer .copyright p {
font-size: 10px;
text-align: left;
margin: 0;
}
.footer-socials {
display: flex;
/* justify-content: center; */
gap: 12px;
margin: 16px 0;
flex-wrap: wrap;
}
.footer-socials .icon {
width: 32px;
height: 32px;
--fill-0: #ffffff;
}
.footer-socials .icon circle {
fill: #ffffff;
fill-opacity: 0.85;
}
.footer-socials .icon .icon-cutout {
fill: #0e1b42;
}
footer .footer-links {
padding: 0 16px;
}
footer .footer-links li {
list-style-type: none;
}
footer .footer-links a {
font-size: 14px;
color: #ffffff;
}
footer .footer-divider {
align-self: stretch;
display: flex;
align-items: center;
padding: 0 8px;
width: 1px;
border-left: 1px solid #ffffff;
}
footer .footer-sections {
display: flex;
gap: 32px;
flex-wrap: wrap;
}
@media (max-width: 768px) {
.footer-inner {
flex-direction: column;
align-items: center;
text-align: center;
}
footer .company-info,
footer .footer-links {
max-width: 100%;
}
.footer-socials {
justify-content: center;
}
footer .footer-sections {
justify-content: center;
}
footer .footer-divider {
width: 100%;
padding: 12px 0;
border-left: 0;
border-top: 1px solid #ffffff;
}
footer .copyright p {
text-align: center;
}
}

View File

@ -1,37 +0,0 @@
document.addEventListener("DOMContentLoaded", function () {
var toggle = document.querySelector(".menu-toggle");
var nav = document.querySelector(".main-nav");
if (!toggle || !nav) {
return;
}
toggle.addEventListener("click", function () {
var isOpen = nav.classList.toggle("is-open");
toggle.classList.toggle("is-open", isOpen);
toggle.setAttribute("aria-expanded", isOpen ? "true" : "false");
});
var submenuToggles = document.querySelectorAll(".submenu-toggle");
submenuToggles.forEach(function (button) {
button.addEventListener("click", function () {
var item = button.closest(".menu-item");
if (!item) {
return;
}
var isOpen = item.classList.contains("is-open");
document.querySelectorAll(".menu-item.is-open").forEach(function (openItem) {
if (openItem !== item) {
openItem.classList.remove("is-open");
var openButton = openItem.querySelector(".submenu-toggle");
if (openButton) {
openButton.setAttribute("aria-expanded", "false");
}
}
});
item.classList.toggle("is-open", !isOpen);
button.setAttribute("aria-expanded", !isOpen ? "true" : "false");
});
});
});

View File

@ -36,17 +36,12 @@
{% include "includes/header.html" %}
<main class="site-main">
<div class="site-container">
{% block content %}{% endblock %}
</div>
</main>
{% block content %}{% endblock %}
{% include "includes/footer.html" %}
{# Global javascript #}
<script type="text/javascript" src="{% static 'js/mysite.js' %}"></script>
<script type="text/javascript" src="{% static 'js/header.js' %}"></script>
{# Instagram embed script to render IG oEmbeds #}
<script async src="https://www.instagram.com/embed.js"></script>

View File

@ -1,75 +1,32 @@
{% load navigation_tags %}
<footer>
<div class="site-container footer-inner">
<div class="company-info">
<div class="copyright">
{% get_footer_text %}
</div>
{% with social_links=settings.base.SocialMediaSettings.links %}
{% if social_links %}
<div class="footer-socials" aria-label="social icons">
{% for item in social_links %}
<a href="{{ item.value.url }}" target="_blank" aria-label="{{ item.value.platform }}">
{% if item.value.platform|lower == "facebook" %}
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33.18 33.18" aria-label="facebook" role="img">
<path d="M16.59 0C7.43 0 0 7.43 0 16.59C0 25.75 7.43 33.18 16.59 33.18C25.75 33.18 33.18 25.75 33.18 16.59C33.18 7.43 25.75 0 16.59 0ZM23.12 8.37H21.18C19.27 8.37 18.67 9.56 18.67 10.78V13.67H22.94L22.26 18.12H18.67V28.89H13.85V18.12H9.94V13.67H13.85V10.28C13.85 6.42 16.15 4.29 19.67 4.29C21.36 4.29 23.12 4.59 23.12 4.59V8.38V8.37Z" fill="var(--fill-0, #0E1B42)" id="Vector"/>
</svg>
{% elif item.value.platform|lower == "instagram" %}
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33.18 33.18" aria-label="instagram" role="img">
<path d="M16.67 13.42C14.92 13.42 13.5 14.84 13.5 16.59C13.5 18.34 14.92 19.76 16.67 19.76C18.42 19.76 19.84 18.34 19.84 16.59C19.84 14.84 18.42 13.42 16.67 13.42Z" fill="var(--fill-0, #0E1B42)" id="Vector"/>
<path d="M20.6 8.42H12.57C10.28 8.42 8.42 10.28 8.42 12.57V20.6C8.42 22.89 10.28 24.75 12.57 24.75H20.6C22.89 24.75 24.75 22.89 24.75 20.6V12.57C24.75 10.28 22.89 8.42 20.6 8.42ZM16.67 21.54C13.94 21.54 11.72 19.32 11.72 16.59C11.72 13.86 13.94 11.64 16.67 11.64C19.4 11.64 21.62 13.86 21.62 16.59C21.62 19.32 19.4 21.54 16.67 21.54ZM21.91 12.48C21.26 12.48 20.73 11.95 20.73 11.3C20.73 10.65 21.26 10.12 21.91 10.12C22.56 10.12 23.09 10.65 23.09 11.3C23.09 11.95 22.56 12.48 21.91 12.48Z" fill="var(--fill-0, #0E1B42)" id="Vector_2"/>
<path d="M16.59 0C7.43 0 0 7.43 0 16.59C0 25.75 7.43 33.18 16.59 33.18C25.75 33.18 33.18 25.75 33.18 16.59C33.18 7.43 25.75 0 16.59 0ZM26.64 20.6C26.64 23.93 23.93 26.64 20.6 26.64H12.57C9.24 26.64 6.53 23.93 6.53 20.6V12.57C6.53 9.24 9.24 6.53 12.57 6.53H20.6C23.93 6.53 26.64 9.24 26.64 12.57V20.6Z" fill="var(--fill-0, #0E1B42)" id="Vector_3"/>
</svg>
{% elif item.value.platform|lower == "youtube" %}
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33.18 33.18" aria-label="youtube" role="img">
<path d="M20.36 16.45L15.14 13.61C14.93 13.49 14.19 13.64 14.19 13.89V19.43C14.19 19.67 14.92 19.83 15.13 19.71L20.59 17.01C20.81 16.89 20.59 16.57 20.36 16.45Z" fill="var(--fill-0, #0E1B42)" id="Vector"/>
<path d="M16.59 0C7.43 0 0 7.43 0 16.59C0 25.75 7.43 33.18 16.59 33.18C25.75 33.18 33.18 25.75 33.18 16.59C33.18 7.43 25.75 0 16.59 0ZM27.74 19.42C27.74 22.05 25.61 24.18 22.98 24.18H10.81C8.18 24.18 6.05 22.05 6.05 19.42V13.76C6.05 11.13 8.18 9 10.81 9H22.98C25.61 9 27.74 11.13 27.74 13.76V19.42Z" fill="var(--fill-0, #0E1B42)" id="Vector_2"/>
</svg>
{% elif item.value.platform|lower == "threads" %}
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33.18 33.18" aria-label="threads" role="img">
<path d="M16.59 0C7.43 0 0 7.43 0 16.59C0 25.75 7.43 33.18 16.59 33.18C25.75 33.18 33.18 25.75 33.18 16.59C33.18 7.43 25.75 0 16.59 0Z" fill="var(--fill-0, #0E1B42)"/>
<path class="icon-cutout" d="M6.321 6.016c-.27-.18-1.166-.802-1.166-.802.756-1.081 1.753-1.502 3.132-1.502.975 0 1.803.327 2.394.948s.928 1.509 1.005 2.644q.492.207.905.484c1.109.745 1.719 1.86 1.719 3.137 0 2.716-2.226 5.075-6.256 5.075C4.594 16 1 13.987 1 7.994 1 2.034 4.482 0 8.044 0 9.69 0 13.55.243 15 5.036l-1.36.353C12.516 1.974 10.163 1.43 8.006 1.43c-3.565 0-5.582 2.171-5.582 6.79 0 4.143 2.254 6.343 5.63 6.343 2.777 0 4.847-1.443 4.847-3.556 0-1.438-1.208-2.127-1.27-2.127-.236 1.234-.868 3.31-3.644 3.31-1.618 0-3.013-1.118-3.013-2.582 0-2.09 1.984-2.847 3.55-2.847.586 0 1.294.04 1.663.114 0-.637-.54-1.728-1.9-1.728-1.25 0-1.566.405-1.967.868ZM8.716 8.19c-2.04 0-2.304.87-2.304 1.416 0 .878 1.043 1.168 1.6 1.168 1.02 0 2.067-.282 2.232-2.423a6.2 6.2 0 0 0-1.528-.161" transform="translate(7.59 7.59) scale(1.125)"/>
</svg>
{% elif item.value.platform|lower == "linkedin" %}
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33.18 33.18" aria-label="linkedin" role="img">
<path d="M16.59 0C7.43 0 0 7.43 0 16.59C0 25.75 7.43 33.18 16.59 33.18C25.75 33.18 33.18 25.75 33.18 16.59C33.18 7.43 25.75 0 16.59 0Z" fill="var(--fill-0, #0E1B42)"/>
<path class="icon-cutout" d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z" transform="translate(7.59 7.59) scale(1.125)"/>
</svg>
{% elif item.value.platform|lower == "x" or item.value.platform|lower == "twitter" or item.value.platform|lower == "twitter-x" %}
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33.18 33.18" aria-label="twitter-x" role="img">
<path d="M16.59 0C7.43 0 0 7.43 0 16.59C0 25.75 7.43 33.18 16.59 33.18C25.75 33.18 33.18 25.75 33.18 16.59C33.18 7.43 25.75 0 16.59 0Z" fill="var(--fill-0, #0E1B42)"/>
<path class="icon-cutout" d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z" transform="translate(7.59 7.59) scale(1.125)"/>
</svg>
{% else %}
{{ item.value.platform }}
{% endif %}
</a>
{% if settings.base.NavigationSettings.footer_links %}
<div class="footer-sections">
{% for section in settings.base.NavigationSettings.footer_links %}
<div class="footer-section">
{% if section.value.title %}
<h3>{{ section.value.title }}</h3>
{% endif %}
<ul>
{% for link in section.value.links %}
<li><a href="{{ link.url }}" target="_blank">{{ link.label }}</a></li>
{% endfor %}
</div>
{% endif %}
{% endwith %}
</div>
<div class="footer-divider" aria-hidden="true"></div>
<div class="footer-links">
{% if settings.base.NavigationSettings.footer_links %}
<div class="footer-sections">
{% for section in settings.base.NavigationSettings.footer_links %}
<div class="footer-section">
{% if section.value.title %}
<h3>{{ section.value.title }}</h3>
{% endif %}
<ul>
{% for link in section.value.links %}
<li><a href="{{ link.url }}" target="_blank">{{ link.label }}</a></li>
{% endfor %}
</ul>
</div>
{% endfor %}
</ul>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
{% with social_links=settings.base.SocialMediaSettings.links %}
{% if social_links %}
<p>Follow us
{% for item in social_links %}
<a href="{{ item.value.url }}" target="_blank" alt="{{ item.value.platform }}">{{ item.value.platform }}</a>
{% endfor %}
</p>
{% endif %}
{% endwith %}
{% get_footer_text %}
</footer>

View File

@ -1,98 +1,45 @@
{% load wagtailsettings_tags wagtailimages_tags %}
{% get_settings use_default_site=True as settings %}
<header class="site-header{% if settings.base.HeaderSettings.logo_light and settings.base.HeaderSettings.logo_dark %} has-logo-variants{% endif %}">
<div class="site-container">
<div class="header-inner">
{% if settings.base.HeaderSettings.logo_light and settings.base.HeaderSettings.logo_dark %}
<a href="/" class="logo logo--light">
{% if settings.base.HeaderSettings.site_name %}
{% image settings.base.HeaderSettings.logo_light fill-217x30 alt=settings.base.HeaderSettings.site_name %}
{% else %}
{% image settings.base.HeaderSettings.logo_light fill-217x30 %}
{% endif %}
</a>
<a href="/" class="logo logo--dark">
{% if settings.base.HeaderSettings.site_name %}
{% image settings.base.HeaderSettings.logo_dark fill-217x30 alt=settings.base.HeaderSettings.site_name %}
{% else %}
{% image settings.base.HeaderSettings.logo_dark fill-217x30 %}
{% endif %}
</a>
{% elif settings.base.HeaderSettings.logo_light %}
<header class="site-header">
<div class="header-inner">
{% if settings.base.HeaderSettings.logo %}
<a href="/" class="logo">
{% image settings.base.HeaderSettings.logo fill-60x60 %}
{% if settings.base.HeaderSettings.site_name %}
{% image settings.base.HeaderSettings.logo_light fill-217x30 alt=settings.base.HeaderSettings.site_name %}
{% else %}
{% image settings.base.HeaderSettings.logo_light fill-217x30 %}
{% endif %}
</a>
{% elif settings.base.HeaderSettings.logo_dark %}
<a href="/" class="logo">
{% if settings.base.HeaderSettings.site_name %}
{% image settings.base.HeaderSettings.logo_dark fill-217x30 alt=settings.base.HeaderSettings.site_name %}
{% else %}
{% image settings.base.HeaderSettings.logo_dark fill-217x30 %}
<span class="site-name">{{ settings.base.HeaderSettings.site_name }}</span>
{% endif %}
</a>
{% endif %}
<nav class="main-nav" id="site-nav">
<ul class="main-menu" id="main-menu">
<nav class="main-nav">
<ul>
{% with site_root=page.get_site.root_page %}
{# Top-level menu: direct children of site root #}
<li class="menu-item">
<div class="menu-item-header">
<a href="#">
<span class="main-menu-link">最新文章</span>
</a>
{% if nav_latest_page or nav_trending_page %}
<button class="submenu-toggle" type="button" aria-expanded="false" aria-label="Toggle submenu"></button>
{% endif %}
</div>
<li>
<a href="#">
最新文章
</a>
{% if nav_latest_page or nav_trending_page %}
<span class="menu-divider" aria-hidden="true"></span>
<ul class="submenu">
{% if nav_latest_page %}
<li class="submenu-item">
<a href="{{ nav_latest_page.url }}">
<span class="submenu-item-link">{{ nav_latest_page.title }}</span>
</a>
</li>
<li><a href="{{ nav_latest_page.url }}">{{ nav_latest_page.title }}</a></li>
{% endif %}
{% if nav_trending_page %}
<li class="submenu-item">
<a href="{{ nav_trending_page.url }}">
<span class="submenu-item-link">{{ nav_trending_page.title }}</span>
</a>
</li>
<li><a href="{{ nav_trending_page.url }}">{{ nav_trending_page.title }}</a></li>
{% endif %}
</ul>
{% endif %}
</li>
{% for menu_page in site_root.get_children.live.in_menu %}
<li class="menu-item">
<div class="menu-item-header">
<a href="{{ menu_page.url }}">
<span class="main-menu-link">{{ menu_page.title }}</span>
</a>
{% with submenu=menu_page.get_children.live.in_menu %}
{% if submenu %}
<button class="submenu-toggle" type="button" aria-expanded="false" aria-label="Toggle submenu"></button>
{% endif %}
{% endwith %}
</div>
<li>
<a href="{{ menu_page.url }}">{{ menu_page.title }}</a>
{# Second-level: direct children of each top-level page #}
{% with submenu=menu_page.get_children.live.in_menu %}
{% if submenu %}
<span class="menu-divider" aria-hidden="true"></span>
<ul class="submenu">
{% for subpage in submenu %}
<li class="submenu-item">
<a href="{{ subpage.url }}">
<span class="submenu-item-link">{{ subpage.title }}</span>
</a>
</li>
<li><a href="{{ subpage.url }}">{{ subpage.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
@ -104,37 +51,20 @@
{# Optional extra links from settings #}
{% if settings.base.HeaderSettings.main_links %}
{% for item in settings.base.HeaderSettings.main_links %}
<li class="menu-item">
<div class="menu-item-header">
<a href="{{ item.value.url }}">
<span class="main-menu-link">{{ item.value.label }}</span>
</a>
</div>
</li>
<li><a href="{{ item.value.url }}">{{ item.value.label }}</a></li>
{% endfor %}
{% endif %}
</ul>
</nav>
<form class="header-search" action="{% url 'search' %}" method="get" role="search">
<div class="search-input">
<button type="submit" class="search-icon" aria-label="搜尋">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M10 4a6 6 0 104.24 10.24l4.76 4.76 1.42-1.42-4.76-4.76A6 6 0 0010 4zm0 2a4 4 0 110 8 4 4 0 010-8z"/></svg>
</button>
<input
type="search"
name="query"
placeholder="搜尋文章"
value="{{ request.GET.query|default:'' }}"
aria-label="搜尋文章">
</div>
<input
type="search"
name="query"
placeholder="搜尋文章"
value="{{ request.GET.query|default:'' }}"
aria-label="搜尋文章">
<button type="submit">搜尋</button>
</form>
<button class="menu-toggle" type="button" aria-expanded="false" aria-controls="site-nav" aria-label="Toggle menu">
<span class="menu-toggle-bar"></span>
<span class="menu-toggle-bar"></span>
<span class="menu-toggle-bar"></span>
</button>
</div>
</div>
</header>