人们现在还在 CSS 文件里写 CSS 吗?说实话,我可不像刚入行时那样跟上这些潮流。部分原因是我觉得在线的 web 开发社区已经变得比当初我刚入行时更加 “我的方式最好” 了。当初我刚入行时,正处于 HTML 表格末期和 CSS 浮动布局初期。
特别喜欢我在2024年在Web Directions Code上做的“The State of CSS”演讲中的这张幻灯片:“CSS现状”
不过,撇开个人意见不说,我确实有一些项目涉及在 CSS 文件中使用 CSS。这是一个 Drupal 10 网站,自定义主题也用到了 CSS。你可能会想,到了 2025 年还有谁用 Drupal?显然,Drupal 用在了约 1.2% 的已知使用内容管理系统的网站中,这大约占所有网站的 0.9%。 (https://w3techs.com/technologies/details/cm-drupal) 据估计,目前大约有 1.92 亿个在维护中的网站,所以这仍然算是一个不小的数字。
那这个东西到底是用来干嘛的?这个用例是关于国际化的(i18n)。Drupal 7 的使用时间很长,因此如果你是 Drupal 新手,或者像我这样从 Drupal 7 暂别后才回归的人,直接用 Google 搜索 Drupal 文档,SEO 会有点混乱。
总之,Drupal最新的多语言文档位于第十章:让您的站点多语言化和多语言指南页面。如果您想自己在Drupal 10站点上实现多语言化,请前往这些链接。
我试图解决的具体问题是关于语言切换器的。一旦你在Drupal网站上启用了多语言功能,就会有一个语言切换器模块,你可以把它放在你觉得合适的地方。
我想,这取决于你使用的Drupal主题,这个区块可能有也可能没有相关的样式。但由于我的网站是完全自定义实现,我的主题是基于Starterkit主题。
无需改动默认设置,一旦你在默认主题的页眉区域启用语言切换器模块,你将看到一个包含所有网站支持语言的无序列表。
那些语言标签可以在管理界面内编辑。你需要进入admin/config/regional/language,然后编辑那里列出的每种语言。我的情况只需要两种语言,所以我将语言切换器设计成出现在页眉上的附加链接选项,标签使用每种语言的两位字母代码。
Twig 模板
你不能用传统的CSS而不用CSS类。而在Drupal主题的世界中,这意味着是Twig模板登场的时候了。Drupal中的模板利用了命名约定方便你进行覆盖。
所以如果你是从头开始使用一个starterkit基础主题,你的默认标记将会被包裹在多层的<div>
标签中,我个人不太喜欢这样。我首先会覆盖 page.html.twig 和 region.html.twig 模板,添加一些有用的CSS类名。
# 页面.html.twig
{% if logged_in %}
{{ page.admin }}
{% endif %}
<header class="site-header" role="横幅">
{{ page.header }}
</header>
<main role="主要">
<a id="main-content" tabindex="-1"></a>
<h1 class="visually-hidden">{{ current_page_title }}</h1>
{{ page.content }}
</main>
全屏模式 退出全屏
# region.html.twig
{%
set 类名 = [
'region',
'region-' ~ region|clean_class,
]
%}
{% if content %}
<div{{ attributes.addClass(类名) }}>
{{ content }}
</div>
{% endif %}
进入全屏 退出全屏
自带的菜单列表也没有CSS类,所以说还得给菜单加些自定义CSS类。
# 主菜单模板
{% import _self as menus %}
{{ menus.menu_links(items, attributes, 0) }}
{% macro menu_links(items, attributes, menu_level) %}
{% import _self as menus %}
{% if items %}
{% if menu_level == 0 %}
<ul{{ attributes }} class="站点导航链接 菜单层级-{{ menu_level + 1 }}" id="站点导航菜单">
{% else %}
<ul class="菜单层级-{{ menu_level + 1 }}">
{% endif %}
{% for item in items %}
{%
set classes = [
'菜单项',
item.is_expanded ? '有子菜单',
'菜单项层级-' ~ (menu_level + 1)
]
%}
<li{{ item.attributes.addClass(classes) }}>
{{ link(item.title, item.url) }}
{% if item.below %}
{{ menus.menu_links(item.below, attributes, menu_level + 1) }}
{# 子菜单 #}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
点击此处进入全屏 (进入全屏)
点击此处退出全屏 (退出全屏)
最后,我还想在导航块上加些额外的东西,比如网站的标志和手机上的菜单按钮,这就意味着需要替换掉主菜单块。
# block--blank-main-menu.html.twig
{% set heading_id = attributes.id ~ '-menu'|clean_id %}
<nav class="site-nav" role="navigation" aria-labelledby="{{ heading_id }}"{{ attributes|without('role', 'aria-labelledby') }}>
{% if not configuration.label_display %}
{% set title_attributes = title_attributes.addClass('visually-hidden') %}
{% endif %}
{{ title_prefix }}
<h2{{ title_attributes.setAttribute('id', heading_id) }}>{{ configuration.label }}</h2>
{{ title_suffix }}
<a href="{{ path('<front>') }}" rel="home" class="site-logo">
<img class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="{{ site_logo }}" alt="{{ '主页'|t }}" fetchpriority="high" />
</a>
<div class="site-links-wrapper" data-nav-wrapper id="siteNavLinks">
{{ content }}
</div>
<button
type="button"
class="site-nav__toggle"
aria-controls="siteNavMenu"
aria-label="菜单切换"
title="菜单切换"
id="siteNavToggle"
>
<div id="menuIcon" class="menu-icon">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</button>
</nav>
切换到全屏模式,退出全屏模式
捣鼓完模板后记得执行 drush cr
。
CSS 的时间
那之后基本上就是CSS了。主要是围绕着使用flexbox。不过让我们看看这样看一遍是否对那些不再编写“传统CSS”的人来说是否容易理解,不管这代表什么意思。
我希望我的头部在用户滚动页面时始终固定在顶部。这里的关键是要记得设置 top
值,否则头部就不会固定在顶部了。
.site-header {
padding: 0 var(--space-m); /* 内边距: 0 var(--space-m); */
position: sticky; /* 悬停定位 */
top: 0;
z-index: 2;
box-shadow: var(--box-shadow); /* 阴影: var(--box-shadow); */
background-color: white; /* 背景颜色: white; */
}
点击进入全屏,点击退出全屏
我有一个带有 .site-nav
类的 <nav>
元素,它包含了网站的 logo、导航链接和移动菜单切换。让我们将它设置成 flexbox 布局,并使内部内容居中,再加上一些基本的悬停效果。
.site-nav {
display: flex;
align-items: center;
}
.site-logo {
flex: none;
}
.site-nav__links {
list-style: none;
padding-inline-start: 0;
}
.site-nav__links a {
display: block;
color: currentColor;
transition: text-decoration 200ms ease-in-out;
white-space: nowrap;
text-underline-offset: 8px;
text-decoration: underline 1.5px transparent;
padding: var(--space-s) var(--space-2xs);
}
.site-nav__links a:hover {
background-color: ghostwhite;
}
.site-nav__links .is-active {
text-decoration-color: currentColor;
}
全屏 退出全屏
关于语言切换选项,默认情况下,它是一个简单的列表,但我们完全可以稍微改进一下。
.language-switcher-language-url {
flex: none;
}
.language-switcher-language-url .links {
display: flex;
list-style: none;
padding: 0;
margin-inline-start: var(--space-2xs);
}
.language-switcher-language-url .links li:first-child::after {
display: inline-flex;
content: "|";
}
.language-switcher-language-url .links a {
padding: var(--space-s) var(--space-2xs);
}
全屏模式;退出全屏
此时,你的页眉仍然看起来不协调。不用担心,这很正常。一切都会好起来的。
当屏幕空间足够大的时候,比如说宽度至少有700像素的时候,大家就可以挤在一行里。
@media screen and (min-width: 700px) {
.region-header {
display: flex;
align-items: center;
}
.site-links-wrapper {
flex: 1;
}
.site-nav {
flex: 1;
}
.site-nav__links {
display: flex;
justify-content: center;
}
.site-nav__toggle {
display: none;
}
}
全屏模式 退出全屏
这些布局样式确保链接列表在站点logo和语言切换器之后占据剩下的空间。
这是宽视窗标题的图片。
无需修改更多的标记,我们可以加入一些JavaScript代码来帮助构建一个移动菜单。但首先,我们需要调整一下样式。
@media screen and (max-width: 699px) {
.site-links-wrapper {
position: absolute;
background-color: white;
top: var(--site-header-height);
right: 0;
transition: transform 300ms ease-in-out;
border-block-start: 1px solid lightgrey;
}
.site-links-wrapper.offscreen {
transform: translateX(100%);
}
.site-links-wrapper a {
padding-inline: var(--space-s);
}
.language-switcher-language-url {
position: absolute;
top: 25px;
right: calc(var(--space-m) + var(--space-xs) + 24px);
}
}
切换到全屏模式,退出全屏
这将链接列表中的链接绝对定位在站点标题正下方,并固定在视口右侧。如果你对为什么语言切换按钮也被绝对定位感到好奇,那是因为这种标记结构方式。
如上所述,语言切换器是一个Drupal系统区块。能不能把主菜单模版改一改,把语言切换器加进去?相比之下,我选择了用CSS的方式来实现。这里的“主菜单模版”可以理解为“改造主菜单模版”,更符合原意。
所以我们将语言切换器放置在移动菜单切换按钮旁边。我觉得,这算是两害相权取其轻,一种权衡后的选择。
手机端菜单切换按钮的样式大致就是这样,也就是三条黑色的线,当图标加上 open
类时,这些线会变成一个叉形。
.site-nav__toggle {
border: 0;
background: initial;
padding: var(--space-xs) 0;
color: currentColor;
margin-inline-start: auto;
}
.menu-icon {
position: relative;
transform: rotate(0deg);
transition: 0.5s ease-in-out;
cursor: pointer;
height: 1em;
width: 1.5em;
}
.menu-icon span {
display: block;
position: absolute;
height: 4px;
width: 100%;
background: currentColor;
border-radius: 4px;
opacity: 1;
left: 0;
transform: rotate(0deg);
transition: 0.25s ease-in-out;
}
.menu-icon span:nth-child(1) {
top: 0;
}
.menu-icon span:nth-child(2),
.menu-icon span:nth-child(3) {
top: 50%;
}
.menu-icon span:nth-child(4) {
top: 100%;
}
.menu-icon.open span:nth-child(1),
.menu-icon.open span:nth-child(4) {
top: 50%;
width: 0%;
left: 50%;
}
.menu-icon.open span:nth-child(2) {
transform: rotate(45deg);
}
.menu-icon.open span:nth-child(3) {
transform: rotate(-45deg);
}
全屏 退出全屏
一些 JavaScript。
打开和关闭移动菜单需要一些JavaScript来实现。
// 站点导航CSS类定义
const siteLinksWrapper = document.querySelector("[data-nav-wrapper]");
const wideNavMinWidth = window.matchMedia("(min-width: 700px)");
handleNavDisplayStyles(wideNavMinWidth);
wideNavMinWidth.addEventListener("change", handleNavDisplayStyles);
const siteNavToggle = document.getElementById("siteNavToggle");
const siteNavLinks = document.getElementById("siteNavLinks");
const menuIcon = document.getElementById("menuIcon");
if (document.body.contains(siteNavToggle)) {
siteNavToggle.addEventListener("click", handleMobileNavToggle, false);
}
function handleMobileNavToggle(event) {
siteLinksWrapper.classList.toggle("offscreen");
if (siteLinksWrapper.getAttribute("class").includes("offscreen")) {
menuIcon.classList.remove("open");
} else {
menuIcon.classList.add("open");
}
}
function handleNavDisplayStyles(event) {
if (event.matches) {
siteLinksWrapper.classList.remove("offscreen");
} else {
if (siteLinksWrapper) {
flashPrevention(siteLinksWrapper);
}
siteLinksWrapper.classList.add("offscreen");
}
}
function flashPrevention(element) {
element.setAttribute("style", "display:none");
setTimeout(() => {
element.removeAttribute("style");
}, 10);
}
全屏 退出全屏
经过一番波折,最终就成了这样。
您的浏览器不支持嵌入式视频,别担心,您可以下载它并用您最喜欢的视频播放器观看。
收尾这真的超级有趣,尤其是当我搭建一个全新的Drupal环境来确保所有主题代码不受其他组件影响的时候。如果还有人使用Drupal,或者还在写“老派”的CSS,你们能告诉我吗?我觉得和你们有种特别的共鸣。 😬
注释:图片来源自 CraftPix.net 2D Game Assets
共同學習,寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章