Compare commits

...

2 Commits

Author SHA1 Message Date
Warren Chen
4679cc70ef Add subscription floating action button with toggle functionality
- Implemented a floating action button (FAB) for newsletter subscription in the template.
- Added JavaScript to handle the toggle state of the FAB and close it on outside clicks or Escape key press.
- Created CSS styles for the FAB, including animations and responsive design.
- Added a Django template tag to return a random default cover image for the FAB.
- Integrated a form for email input and submission within the FAB.
2026-03-06 18:54:33 +09:00
Warren Chen
8a0621d1ce feat(footer): Add footer component with responsive design and social media links 2026-03-06 14:26:19 +09:00
24 changed files with 880 additions and 335 deletions

View File

@ -0,0 +1,173 @@
.newsletter-page {
background: #0e1b42 url("../img/newsletter_bg.png") center center / cover no-repeat fixed;
}
.newsletter-page .site-main > .site-container {
padding-top: 64px;
padding-bottom: 64px;
min-height: 400px;
}
.newsletter-status {
max-width: 640px;
margin: 0 auto;
padding: 24px;
color: #ffffff;
}
.newsletter-status h1 {
font-size: 32px;
margin-top: 0;
}
.newsletter-status p {
margin: 0;
font-size: 20px;
line-height: 1.5;
}
.newsletter-status-message {
margin-bottom: 16px;
}
.newsletter-unsubscribe-form label {
display: block;
margin-bottom: 8px;
}
.newsletter-unsubscribe-form input[type="email"],
.newsletter-unsubscribe-form input[type="text"] {
width: 300px;
padding: 8px;
border: 0;
background: #ffffff4d;
color: #ffffff;
}
.newsletter-unsubscribe-form button {
width: 131px;
height: 49px;
border: 0;
background: #ffffff;
font-size: 14px;
font-weight: 500;
color: #000000;
padding: 10px 16px;
cursor: pointer;
margin-top: 12px;
}
.newsletter-back-link {
display: inline-flex;
align-items: center;
gap: 16px;
margin-top: 20px;
color: #ffffff;
text-decoration: none;
}
.newsletter-back-link__icon {
position: relative;
width: 52px;
height: 52px;
background: #ffffff80;
}
.newsletter-back-link__icon::before,
.newsletter-back-link__icon::after {
content: "";
position: absolute;
left: 18px;
width: 15px;
height: 1.5px;
background: #0e1b42;
}
.newsletter-back-link__icon::before {
top: 21px;
transform: rotate(-45deg);
}
.newsletter-back-link__icon::after {
top: 31px;
transform: rotate(45deg);
}
.newsletter-back-link__text {
font-size: 16px;
line-height: 1.2;
}
@media (min-width: 575px) and (max-width: 767px) {
.newsletter-page .site-main > .site-container {
min-height: 360px;
}
.newsletter-status {
padding: 16px;
}
.newsletter-status h1 {
font-size: 24px;
}
.newsletter-status p {
font-size: 16px;
}
.newsletter-unsubscribe-form input[type="email"],
.newsletter-unsubscribe-form input[type="text"] {
width: 240px;
}
}
@media (max-width: 574px) {
.newsletter-page .site-main > .site-container {
min-height: 320px;
}
.newsletter-status {
padding: 16px;
}
.newsletter-status h1 {
font-size: 20px;
}
.newsletter-status p {
font-size: 14px;
}
.newsletter-unsubscribe-form input[type="email"],
.newsletter-unsubscribe-form input[type="text"] {
width: 260px;
}
.newsletter-unsubscribe-form button {
width: 90px;
height: 31px;
padding: 4px 16px;
}
.newsletter-back-link__icon {
width: 40px;
height: 40px;
}
.newsletter-back-link__icon::before {
top: 15px;
}
.newsletter-back-link__icon::after {
top: 25px;
}
.newsletter-back-link__icon::before,
.newsletter-back-link__icon::after {
left: 12px;
}
.newsletter-back-link__text {
font-size: 14px;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -1,9 +1,19 @@
{% extends "base.html" %}
{% load static %}
{% block body_class %}template-darkbackground newsletter-page{% endblock %}
{% block extra_css %}
<link rel="stylesheet" type="text/css" href="{% static 'css/newsletter.css' %}">
{% endblock %}
{% block content %}
<section class="newsletter-status{% if success %} is-success{% else %} is-failure{% endif %}">
<h1>{{ title }}</h1>
<div class="newsletter-status-message">{{ message|safe }}</div>
<p><a href="/">回到首頁</a></p>
<a class="newsletter-back-link" href="/">
<span class="newsletter-back-link__icon" aria-hidden="true"></span>
<span class="newsletter-back-link__text">回到首頁</span>
</a>
</section>
{% endblock %}

View File

@ -1,14 +1,20 @@
{% extends "base.html" %}
{% load static %}
{% block body_class %}template-darkbackground newsletter-page{% endblock %}
{% block extra_css %}
<link rel="stylesheet" type="text/css" href="{% static 'css/newsletter.css' %}">
{% endblock %}
{% block content %}
<section class="newsletter-status{% if can_submit %} is-warning{% else %} is-failure{% endif %}">
<h1>取消訂閱</h1>
<h1>很遺憾聽到您取消電子報訂閱</h1>
<div class="newsletter-status-message">{{ intro_message|safe }}</div>
{% if can_submit %}
<form method="post" action="{% url 'newsletter_unsubscribe' %}" class="newsletter-unsubscribe-form">
{% csrf_token %}
<label for="{{ form.email.id_for_label }}">退訂 Email</label>
{{ form.email }}
{{ form.token }}
<button type="submit">確認退訂</button>
@ -17,6 +23,9 @@
<p>退訂連結已失效或缺少必要參數。</p>
{% endif %}
<p><a href="/">回到首頁</a></p>
<a class="newsletter-back-link" href="/">
<span class="newsletter-back-link__icon" aria-hidden="true"></span>
<span class="newsletter-back-link__text">回到首頁</span>
</a>
</section>
{% endblock %}

View File

@ -191,8 +191,8 @@ def newsletter_subscribe(request):
request,
"base/newsletter/status.html",
_build_context(
title="請前往信箱確認",
message="我們已寄出確認信,請點擊信中的連結完成訂閱。",
title="訂閱確認已送出!",
message="感謝您的訂閱<br>下一步,請前往訂閱的信箱收取確認信函<br><br>在信函中點擊連結",
success=True,
),
)
@ -209,7 +209,7 @@ def newsletter_confirm(request):
request,
"base/newsletter/status.html",
_build_context(
title="訂閱認失敗",
title="訂閱失敗",
message=template_settings.confirm_failure_template,
success=False,
),
@ -222,7 +222,7 @@ def newsletter_confirm(request):
request,
"base/newsletter/status.html",
_build_context(
title="訂閱認成功",
title="訂閱成功",
message=template_settings.confirm_success_template,
success=True,
),
@ -232,7 +232,7 @@ def newsletter_confirm(request):
request,
"base/newsletter/status.html",
_build_context(
title="訂閱認失敗",
title="訂閱失敗",
message=template_settings.confirm_failure_template,
success=False,
),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@ -1,5 +1,5 @@
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags static %}
{% load wagtailcore_tags wagtailimages_tags static home_tags %}
{% block extra_css %}
<link rel="stylesheet" type="text/css" href="{% static 'css/article_page.css' %}">
@ -89,7 +89,8 @@
{% image related.cover_image max-194x133 as related_cover %}
<img src="{{ related_cover.url }}" alt="{{ related.title }}">
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ related.title }}">
{% random_default_cover as default_cover %}
<img src="{{ default_cover }}" alt="{{ related.title }}">
{% endif %}
</a>
<p class="related-article-date">{{ related.date|date:"Y/m/d" }}</p>

View File

@ -1,4 +1,4 @@
{% load wagtailimages_tags static %}
{% load wagtailimages_tags static home_tags %}
<div class="site-hero-band full-bleed">
<div class="site-container">
@ -11,7 +11,8 @@
{% image first_article.cover_image max-410x293 as cover %}
<img src="{{ cover.url }}" alt="{{ first_article.title }}"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ first_article.title }}"/>
{% random_default_cover as default_cover %}
<img src="{{ default_cover }}" alt="{{ first_article.title }}"/>
{% endif %}
</a>
</div>
@ -50,7 +51,8 @@
{% image article.cover_image max-410x218 as cover %}
<img src="{{ cover.url }}" alt="{{ article.title }}"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ article.title }}"/>
{% random_default_cover as default_cover %}
<img src="{{ default_cover }}" alt="{{ article.title }}"/>
{% endif %}
</div>
<div class="article-content">
@ -61,4 +63,4 @@
</a>
{% endfor %}
{% endif %}
</div>
</div>

View File

@ -1,4 +1,4 @@
{% load wagtailimages_tags static %}
{% load wagtailimages_tags static home_tags %}
{% block extra_css %}
<link rel="stylesheet" type="text/css" href="{% static 'css/block_list.css' %}">
{% endblock %}
@ -13,7 +13,8 @@
{% image first_article.cover_image max-480x293 as cover %}
<img src="{{ cover.url }}" alt="{{ first_article.title }}"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ first_article.title }}"/>
{% random_default_cover as default_cover %}
<img src="{{ default_cover }}" alt="{{ first_article.title }}"/>
{% endif %}
</a>
</div>

View File

@ -1,4 +1,4 @@
{% load wagtailimages_tags static %}
{% load wagtailimages_tags static home_tags %}
{% block extra_css %}
<link rel="stylesheet" type="text/css" href="{% static 'css/block_list_lower.css' %}">
{% endblock %}
@ -12,7 +12,8 @@
{% image article.cover_image max-194x133 as cover %}
<img src="{{ cover.url }}" alt="{{ article.title }}"/>
{% else %}
<img src="{% static 'img/default_cover.jpg' %}" alt="{{ article.title }}"/>
{% random_default_cover as default_cover %}
<img src="{{ default_cover }}" alt="{{ article.title }}"/>
{% endif %}
</div>
<div><span class="article-title">{{ article.title }}</span></div>

View File

@ -1,4 +1,4 @@
{% load wagtailimages_tags static %}
{% load wagtailimages_tags static home_tags %}
<div class="horizontal-list-wrap" data-horizontal-list>
<button class="horizontal-list-arrow is-hidden" type="button" data-dir="left" aria-label="上一頁">
@ -15,7 +15,8 @@
{% 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"/>
{% random_default_cover as default_cover %}
<img src="{{ default_cover }}" alt="{{ article.title }}" height="133" width="194"/>
{% endif %}
</div>
<div><span class="article-title">{{ article.title }}</span></div>

View File

@ -1,4 +1,4 @@
{% load wagtailimages_tags static %}
{% load wagtailimages_tags static home_tags %}
<div class="news-list-wrap" data-news-list>
<div class="news-hero">
@ -30,7 +30,8 @@
{% image first_article.cover_image max-480x293 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"/>
{% random_default_cover as default_cover %}
<img src="{{ default_cover }}" alt="{{ first_article.title }}" height="293" width="480"/>
{% endif %}
</a>
</div>
@ -65,7 +66,8 @@
{% 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"/>
{% random_default_cover as default_cover %}
<img src="{{ default_cover }}" alt="{{ article.title }}" height="133" width="194"/>
{% endif %}
</div>
<div><span class="article-title">{{ article.title }}</span></div>

View File

@ -0,0 +1,16 @@
import random
from django import template
from django.templatetags.static import static
register = template.Library()
@register.simple_tag
def random_default_cover():
choices = (
"img/default_cover_1.png",
"img/default_cover_2.png",
"img/default_cover_3.png",
)
return static(random.choice(choices))

View File

@ -0,0 +1,261 @@
.site-footer {
background: #0e1b42;
color: #ffffff;
padding: 28px 0 20px;
font-size: 14px;
}
.footer-shell {
display: flex;
flex-direction: column;
align-items: center;
}
.footer-ad-slot {
width: 100%;
}
.footer-separator {
width: 100%;
max-width: 790px;
height: 1px;
background: #ffffff4d;
margin: 0 auto 40px;
}
.footer-main {
width: 100%;
max-width: 790px;
display: grid;
grid-template-columns: 220px 220px 180px;
grid-template-areas:
"b1 b2 b3"
"b1 b2 b4";
column-gap: 40px;
row-gap: 20px;
align-items: start;
}
.footer-col--brand {
grid-area: b1;
min-width: 0;
}
.footer-brand-content img {
max-width: 100%;
height: auto;
margin-bottom: 12px;
}
.footer-brand-content p {
margin: 0 0 8px;
font-size: 12px;
line-height: 1.75;
}
.footer-col--menu {
grid-area: b2;
min-width: 0;
}
.footer-menu-list {
margin: 0;
padding: 0;
list-style: none;
column-count: 2;
column-gap: 40px;
}
.footer-menu-list li {
break-inside: avoid;
margin: 0 0 14px;
}
.footer-menu-list a {
color: #ffffff;
text-decoration: none;
line-height: 1.35;
}
.footer-menu-list a:hover {
text-decoration: underline;
}
.footer-fixed-links{
grid-area: b3;
width: 100%;
}
.footer-fixed-links ul{
margin: 0;
padding: 0;
list-style: none;
display: flex;
justify-content: space-between;
}
.footer-fixed-links a {
font-size: 10px;
line-height: 1.35;
}
.footer-fixed-links a:hover {
text-decoration: underline;
}
.footer-socials {
grid-area: b4;
width: 100%;
display: flex;
justify-content: space-between;
}
.footer-socials .icon {
width: 25px;
height: 25px;
--fill-0: #ffffff;
}
.footer-socials .icon .icon-cutout {
fill: #0e1b42;
}
.footer-powered {
margin-top: 24px;
text-align: center;
font-size: 12px;
font-weight: 100;
line-height: 1.35;
}
@media (min-width: 1024px) {
.footer-fixed-links,
.footer-socials {
align-self: end;
}
}
@media (min-width: 768px) and (max-width: 1023px) {
.footer-separator {
max-width: 616px;
}
.footer-main {
max-width: 616px;
display: grid;
grid-template-columns: 220px 150px 1fr;
grid-template-areas:
"b1 b2 b2"
"b1 b4 b3";
gap: 32px;
}
.footer-col--right:nth-child(3) {
flex-direction: row;
align-items: flex-start;
}
.footer-menu-list {
column-count: 3;
}
}
@media (max-width: 767px) {
.site-footer {
padding: 24px 0 16px;
}
.site-footer .site-container {
max-width: 494px;
}
.footer-separator {
max-width: 494px;
}
.footer-main {
max-width: 494px;
display: grid;
grid-template-columns: 220px 150px 1fr;
grid-template-areas:
"b1 b2 b3"
"b1 b4 .";
gap: 20px;
}
.footer-menu-list {
column-gap: 0px;
}
.footer-menu-list li {
margin: 0 0 6px;
}
.footer-col--right {
grid-column: auto;
align-items: flex-start;
gap: 20px;
}
.footer-fixed-links {
flex-wrap: wrap;
gap: 18px;
}
.footer-fixed-links ul {
display: block;
}
.footer-fixed-links ul li {
margin: 0 0 4px;
}
}
@media (max-width: 574px) {
.footer-separator {
max-width: 289px;
}
.footer-main {
max-width: 220px;
margin-inline: auto;
justify-content: center;
grid-template-columns: 1fr;
grid-template-areas:
"b2"
"b1"
"b4"
"b3";
gap: 20px;
}
.footer-col--menu,
.footer-col--right,
.footer-fixed-links,
.footer-socials {
width: 100%;
}
.footer-menu-list {
text-align: center;
}
.footer-fixed-links ul {
display: flex;
flex-wrap: wrap;
gap: 12px 18px;
}
.footer-menu-list a {
font-size: 16px;
}
.footer-fixed-links a {
font-size: 12px;
}
.footer-socials .icon {
width: 35px;
height: 35px;
}
}

View File

@ -66,7 +66,7 @@ a {
.main-menu {
display: flex;
align-items: center;
gap: 24px;
gap: 18px;
list-style: none;
margin: 0;
padding: 0;
@ -276,7 +276,7 @@ a {
border: 0;
background: transparent;
outline: none;
width: 181px;
width: 153px;
}
.template-darkbackground .header-search input[type="search"] {
@ -287,129 +287,6 @@ a {
color: #ffffff88;
}
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;
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;
}
.newsletter-subscribe-form {
margin-top: 16px;
}
.newsletter-subscribe-form label {
display: block;
margin-bottom: 8px;
font-size: 12px;
font-weight: 700;
}
.newsletter-subscribe-controls {
display: flex;
align-items: center;
gap: 8px;
}
.newsletter-subscribe-controls input[type="email"] {
width: 180px;
max-width: 100%;
padding: 8px;
border: 1px solid #ffffff66;
border-radius: 4px;
background: #ffffff;
color: #0e1b42;
}
.newsletter-subscribe-controls button {
border: 1px solid #ffffff;
border-radius: 4px;
background: transparent;
color: #ffffff;
padding: 8px 12px;
cursor: pointer;
}
.newsletter-subscribe-controls button:hover {
background: #ffffff;
color: #0e1b42;
}
footer .footer-links {
padding: 0 16px;
}
footer .footer-links li {
list-style-type: none;
}
footer .footer-links a {
font-size: 14px;
}
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;
}
.block-title {
display: inline-block;
width: 197px;
@ -434,31 +311,6 @@ footer .footer-sections {
color: #0e1b42;
}
.newsletter-status {
max-width: 640px;
margin: 48px auto;
padding: 24px;
border: 1px solid #0e1b4233;
border-radius: 8px;
}
.newsletter-status h1 {
margin-top: 0;
}
.newsletter-status-message {
margin-bottom: 16px;
}
.newsletter-unsubscribe-form button {
border: 0;
border-radius: 4px;
background: #0e1b42;
color: #ffffff;
padding: 10px 16px;
cursor: pointer;
}
@media (max-width: 1023px) {
.site-container {
max-width: 640px;
@ -470,39 +322,6 @@ footer .footer-sections {
}
@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;
}
.newsletter-subscribe-controls {
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;
}
}
@media (min-width: 575px) and (max-width: 767px) {

View File

@ -0,0 +1,116 @@
.subscribe-fab {
--fab-toggle-width: 59px;
position: fixed;
right: 16px;
top: 12%;
transform: translateY(-50%);
z-index: 1000;
min-height: 59px;
}
.subscribe-fab__toggle {
flex: 0 0 var(--fab-toggle-width);
width: var(--fab-toggle-width);
height: 59px;
margin: 0 0 0 10px;
border: 0;
background: transparent;
color: #ffffff66;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
}
.subscribe-fab__icon {
width: 32px;
height: 32px;
transform: scale(1.35);
transform-origin: center;
}
.subscribe-fab__text {
font-size: 10px;
font-weight: 500;
line-height: 1;
}
.subscribe-fab__panel {
position: absolute;
width: 378px;
right: 0;
top: 0;
display: flex;
align-items: center;
min-width: 368px;
height: 59px;
padding: 0 10px 0 0;
border-radius: 36px;
background: #323232;
border: 1px solid #ffffff80;
transform: translateX(calc(100% - var(--fab-toggle-width)));
transition: transform 0.25s ease, background-color 0.2s ease;
}
.subscribe-fab:hover .subscribe-fab__panel {
background: #767676;
}
.subscribe-fab.is-open .subscribe-fab__panel {
background: #767676;
transform: translateX(44px);
}
.subscribe-fab:hover .subscribe-fab__toggle,
.subscribe-fab.is-open .subscribe-fab__toggle {
color: #0e1b42;;
}
.subscribe-fab__form {
display: flex;
align-items: center;
gap: 8px;
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease;
}
.subscribe-fab.is-open .subscribe-fab__form {
opacity: 1;
pointer-events: auto;
}
.subscribe-fab__input {
width: 190px;
height: 30px;
border: 0;
padding: 0 10px;
background: #ffffff80;
color: #0e1b42;
font-size: 12px;
}
.subscribe-fab__input::placeholder {
color: #0e1b4288;
}
.subscribe-fab__submit {
height: 30px;
border: 0;
padding: 0 10px;
background: #ffffffcc;
color: #0e1b42;
font-size: 12px;
cursor: pointer;
}
@media (max-width: 375px) {
.subscribe-fab__input {
width: 150px;
}
.subscribe-fab.is-open .subscribe-fab__panel {
transform: translateX(84px);
}
}

View File

@ -0,0 +1,29 @@
(function () {
const root = document.querySelector("[data-subscribe-fab]");
if (!root) return;
const toggle = root.querySelector(".subscribe-fab__toggle");
if (!toggle) return;
const setOpen = (open) => {
root.classList.toggle("is-open", open);
toggle.setAttribute("aria-expanded", open ? "true" : "false");
};
toggle.addEventListener("click", function () {
const isOpen = root.classList.contains("is-open");
setOpen(!isOpen);
});
document.addEventListener("click", function (event) {
if (!root.contains(event.target)) {
setOpen(false);
}
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
setOpen(false);
}
});
})();

View File

@ -34,6 +34,8 @@
{# Global stylesheets #}
<link rel="stylesheet" type="text/css" href="{% static 'css/mysite.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/footer.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/subscribe_fab.css' %}">
{% block extra_css %}
{# Override this in templates to add extra stylesheets #}
@ -52,10 +54,12 @@
</main>
{% include "includes/footer.html" %}
{% include "includes/subscribe_fab.html" %}
{# Global javascript #}
<script type="text/javascript" src="{% static 'js/mysite.js' %}"></script>
<script type="text/javascript" src="{% static 'js/header.js' %}"></script>
<script type="text/javascript" src="{% static 'js/subscribe_fab.js' %}"></script>
{# Instagram embed script to render IG oEmbeds #}
<script async src="https://www.instagram.com/embed.js"></script>

View File

@ -1,84 +1,97 @@
{% load navigation_tags %}
{% load navigation_tags wagtailcore_tags %}
<footer>
<div class="site-container footer-inner">
<div class="company-info">
<div class="copyright">
{% get_footer_text %}
<footer class="site-footer">
<div class="site-container footer-shell">
<div class="footer-ad-slot" aria-hidden="true"></div>
<div class="footer-separator" aria-hidden="true"></div>
<div class="footer-main">
<div class="footer-col footer-col--brand">
<div class="footer-brand-content">
{% get_footer_text %}
</div>
</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>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<form class="newsletter-subscribe-form" method="post" action="{% url 'newsletter_subscribe' %}">
{% csrf_token %}
<label for="newsletter-email">訂閱電子報</label>
<div class="newsletter-subscribe-controls">
<input id="newsletter-email" type="email" name="email" required placeholder="輸入 Email">
<button type="submit">送出</button>
<nav class="footer-col footer-col--menu" aria-label="Footer main menu">
<ul class="footer-menu-list">
{% if nav_latest_page %}
<li><a href="{{ nav_latest_page.url }}">{{ nav_latest_page.title }}</a></li>
{% else %}
<li><a href="#">最新文章</a></li>
{% endif %}
{% wagtail_site as current_site %}
{% if page %}
{% with site_root=page.get_site.root_page %}
{% for menu_page in site_root.get_children.live.in_menu %}
<li><a href="{{ menu_page.url }}">{{ menu_page.title }}</a></li>
{% endfor %}
{% endwith %}
{% elif current_site %}
{% with site_root=current_site.root_page %}
{% for menu_page in site_root.get_children.live.in_menu %}
<li><a href="{{ menu_page.url }}">{{ menu_page.title }}</a></li>
{% endfor %}
{% endwith %}
{% endif %}
</ul>
</nav>
<!-- <div class="footer-col footer-col--right"> -->
<div class="footer-fixed-links">
<ul>
<li><a href="{% url 'newsletter_subscribe' %}">訂閱電子報</a></li>
<li><a href="#">合作提案</a></li>
<li><a href="#">聯絡我們</a></li>
</ul>
</div>
</form>
</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>
{% 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" rel="noopener noreferrer" 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)"/>
</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)"/>
<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)"/>
<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)"/>
</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)"/>
<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)"/>
</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>
{% endfor %}
</div>
{% endfor %}
</div>
{% endif %}
{% endif %}
{% endwith %}
<!-- </div> -->
</div>
<div class="footer-powered">power by INNOVEDUS</div>
</div>
</footer>

View File

@ -1,4 +1,4 @@
{% load wagtailsettings_tags wagtailimages_tags %}
{% load wagtailsettings_tags wagtailimages_tags wagtailcore_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 %}">
@ -39,67 +39,132 @@
<nav class="main-nav" id="site-nav">
<ul class="main-menu" id="main-menu">
{% 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>
{% 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>
{% 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>
{% endif %}
</ul>
{% endif %}
</li>
{% for menu_page in site_root.get_children.live.in_menu %}
{% wagtail_site as current_site %}
{% if page %}
{% 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="{{ menu_page.url }}">
<span class="main-menu-link">{{ menu_page.title }}</span>
<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>
{% 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>
{% 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>
{% 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>
{# Second-level: direct children of each top-level page #}
{% 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>
<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>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</div>
{# 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>
{% endfor %}
</ul>
</li>
{% endfor %}
{% endwith %}
{% elif current_site %}
{% with site_root=current_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 %}
{% endwith %}
</div>
{% 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>
{% 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>
{% endif %}
</ul>
{% endif %}
</li>
{% endfor %}
{% endwith %}
{% 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>
{# 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>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</li>
{% endfor %}
{% endwith %}
{% endif %}
{# Optional extra links from settings #}
{% if settings.base.HeaderSettings.main_links %}

View File

@ -0,0 +1,22 @@
<div class="subscribe-fab" data-subscribe-fab>
<div id="subscribe-fab-panel" class="subscribe-fab__panel">
<button class="subscribe-fab__toggle" type="button" aria-expanded="false" aria-controls="subscribe-fab-panel">
<svg class="subscribe-fab__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M3 6h18v12H3z" fill="none" stroke="currentColor" stroke-width="0.5"/>
<path d="M3 7l9 7 9-7" fill="none" stroke="currentColor" stroke-width="0.5"/>
</svg>
<span class="subscribe-fab__text">點我訂閱</span>
</button>
<form class="subscribe-fab__form" method="post" action="{% url 'newsletter_subscribe' %}">
{% csrf_token %}
<input
class="subscribe-fab__input"
type="email"
name="email"
required
placeholder="輸入 Email">
<button class="subscribe-fab__submit" type="submit">確認訂閱</button>
</form>
</div>
</div>