卡片字段

正面
<div class="row word">{{Word}}</div>
{{#IPA}}
<div class="row pron">
<span class="ipa">{{IPA}}</span>
{{#Audio}}<span class="audio">{{Audio}}</span>{{/Audio}}
</div>
{{/IPA}}
{{#Audio}}
{{^IPA}}
<div class="row pron">
<span class="audio">{{Audio}}</span>
</div>
{{/IPA}}
{{/Audio}}
<div class="tags">
{{#Exam_Level}}<span class="chip">{{Exam_Level}}</span>{{/Exam_Level}}
{{#COCA_Rank}}<span class="chip">COCA {{COCA_Rank}}</span>{{/COCA_Rank}}
{{#Frq}}<span class="chip">Frq {{Frq}}</span>{{/Frq}}
</div>
背面
<div class="row word">{{Word}}</div>
<div class="row pron">
{{#IPA}}<span class="ipa">{{IPA}}</span>{{/IPA}}
{{#Audio}}<span class="audio">{{Audio}}</span>{{/Audio}}
</div>
<div class="hr"></div>
<div class="row meaning">{{Meaning}}</div>
{{#En_Meaning}}
<div class="row en">{{En_Meaning}}</div>
{{/En_Meaning}}
<div class="tags">
{{#COCA_Rank}}<span class="chip">COCA {{COCA_Rank}}</span>{{/COCA_Rank}}
{{#Frq}}<span class="chip">Frq {{Frq}}</span>{{/Frq}}
{{#Times}}<span class="chip">Seen {{Times}}</span>{{/Times}}
{{#Exam_Level}}<span class="chip">Exam {{Exam_Level}}</span>{{/Exam_Level}}
{{#Video_Url}}<a class="chip link" href="{{Video_Url}}">Video</a>{{/Video_Url}}
</div>
<div class="hr"></div>
<div class="row morph">
{{#Lemma}}<div class="box"><b>Lemma</b><br>{{Lemma}}</div>{{/Lemma}}
{{#Prefix}}<div class="box"><b>Prefix</b><br>{{Prefix}}</div>{{/Prefix}}
{{#Root}}<div class="box"><b>Root</b><br>{{Root}}</div>{{/Root}}
{{#Suffix}}<div class="box"><b>Suffix</b><br>{{Suffix}}</div>{{/Suffix}}
</div>
<!-- 正确做法:直接渲染 Anki 图片 HTML -->
{{#Image}}
<div class="imgwrap" id="imgWrap">
{{Image}}
</div>
{{/Image}}
{{#Tips}}
<div class="tips">
<div class="tips-title">💡 Tips</div>
<div class="tips-content">{{Tips}}</div>
</div>
{{/Tips}}
{{#Example}}
<div class="row example" id="exWrap">{{Example}}</div>
{{/Example}}
<script>
(function () {
function nlToBr(selector) {
var el = document.querySelector(selector);
if (!el) return;
el.innerHTML = el.innerHTML.replace(/\\n/g, "<br>");
}
function isVisiblyEmpty(node) {
if (!node) return true;
if (node.querySelector("img, audio, video, svg, iframe")) return false;
var t = (node.textContent || "")
.replace(/\u00A0/g, "")
.replace(/\u200B/g, "")
.replace(/\u200C/g, "")
.replace(/\u200D/g, "")
.replace(/\uFEFF/g, "")
.replace(/\s+/g, "")
.trim();
return t.length === 0;
}
function normalizeUrl(url) {
return (url || "").trim().replace(/["'<>),]+$/g, "");
}
function looksLikeImageUrl(url) {
if (!url) return false;
var cleanUrl = normalizeUrl(url);
if (/\.(png|jpe?g|gif|webp|bmp|svg)(\?[^\s]*)?$/i.test(cleanUrl)) {
return true;
}
try {
var parsed = new URL(cleanUrl, window.location.href);
var host = (parsed.hostname || "").toLowerCase();
var path = (parsed.pathname || "").toLowerCase();
var query = (parsed.search || "").toLowerCase();
var hintText = host + path + query;
if (!/^https?:$/i.test(parsed.protocol)) {
return false;
}
if (/\/images?(\/|$|\?)/i.test(path)) return true;
if (/^th\.bing\.com$/i.test(host) && /^\/th\/id\//i.test(path)) return true;
if (/image|img|photo|pic|thumb|thumbnail|media|asset|static|upload/.test(hintText)) {
return true;
}
if (/cdn|gstatic|googleusercontent|licdn/.test(host)) return true;
if (/tbn=|q=|w=|h=|fit=|format=|fm=/.test(query) && /(gstatic|cdn|img|image|media)/.test(hintText)) {
return true;
}
} catch (e) {
return false;
}
return false;
}
function createFallbackLink(url) {
var link = document.createElement("a");
link.href = url;
link.target = "_blank";
link.rel = "noopener noreferrer";
link.className = "extlink";
link.textContent = url;
return link;
}
function createImageOrLink(url) {
var cleanUrl = normalizeUrl(url);
if (!cleanUrl) {
return document.createTextNode("");
}
if (!looksLikeImageUrl(cleanUrl)) {
return createFallbackLink(cleanUrl);
}
var img = document.createElement("img");
img.src = cleanUrl;
img.alt = "image";
img.loading = "lazy";
img.referrerPolicy = "no-referrer";
img.onerror = function () {
if (!img.dataset.fallbackApplied) {
img.dataset.fallbackApplied = "1";
img.replaceWith(createFallbackLink(cleanUrl));
}
};
return img;
}
function fixImage() {
var wrap = document.getElementById("imgWrap");
if (!wrap) return;
var nodes = Array.from(wrap.childNodes);
nodes.forEach(function (node) {
if (node.nodeType === Node.TEXT_NODE) {
var text = node.textContent;
var regex = /https?:\/\/[^\s<>"']+/gi;
var match;
var lastIndex = 0;
var frag = document.createDocumentFragment();
while ((match = regex.exec(text)) !== null) {
var rawUrl = match[0];
var url = normalizeUrl(rawUrl);
if (!looksLikeImageUrl(url)) {
continue;
}
frag.appendChild(
document.createTextNode(text.slice(lastIndex, match.index))
);
frag.appendChild(createImageOrLink(url));
lastIndex = match.index + rawUrl.length;
}
if (lastIndex > 0) {
frag.appendChild(document.createTextNode(text.slice(lastIndex)));
node.replaceWith(frag);
}
}
});
var links = Array.from(wrap.querySelectorAll("a[href]"));
links.forEach(function (link) {
var href = normalizeUrl(link.getAttribute("href"));
if (!looksLikeImageUrl(href)) {
return;
}
link.replaceWith(createImageOrLink(href));
});
}
function fix() {
nlToBr(".meaning");
nlToBr(".en");
nlToBr(".tips-content");
var ex = document.getElementById("exWrap");
if (ex && isVisiblyEmpty(ex)) {
ex.style.display = "none";
}
fixImage();
}
fix();
setTimeout(fix, 50);
})();
</script>
CSS
/* ===== Theme ===== */
/* ===== Theme ===== */
:root {
--bg: #faf7f2;
--panel: #fffdf8;
--fg: #1b1b1b;
--muted: #6b6b6b;
--border: #e6e0d6;
--chip-bg: #f2ede6;
--accent: #1f6f78;
--shadow: 0 10px 30px rgba(31, 22, 10, 0.08);
}
.nightMode,
.night_mode,
body.nightMode {
--bg: #121311;
--panel: #171816;
--fg: #e8e7e4;
--muted: #b2ada4;
--border: #2c2d2a;
--chip-bg: #20211f;
--accent: #6fb2b9;
--shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}
/* ===== Base ===== */
.card {
position: relative;
font-family:
"Iowan Old Style",
"Palatino Linotype",
"Source Han Serif SC",
"Songti SC",
"STSong",
"Times New Roman",
serif;
font-size: 19px;
line-height: 1.6;
color: var(--fg);
text-align: left;
padding: 18px 16px;
border: 1px solid color-mix(in srgb, var(--border) 75%, transparent);
border-radius: 18px;
background: linear-gradient(
180deg,
var(--panel) 0%,
color-mix(in srgb, var(--panel) 96%, var(--bg)) 60%,
var(--bg) 100%
);
background-clip: padding-box;
box-shadow: var(--shadow);
animation: fadein 260ms ease-out;
}
@keyframes fadein {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ===== Layout ===== */
.row {
margin: 10px 0;
}
.hr {
height: 1px;
margin: 14px 0;
background: linear-gradient(
90deg,
transparent,
color-mix(in srgb, var(--border) 60%, transparent),
transparent
);
}
/* ===== Word / Pron ===== */
.word {
padding-right: 140px;
font-size: 40px;
font-weight: 800;
letter-spacing: 0.3px;
color: var(--fg);
}
.pron {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
font-size: 17px;
color: var(--muted);
}
.ipa {
font-style: italic;
}
/* ===== Meaning / Example Text ===== */
.meaning {
font-size: 22px;
font-weight: 650;
white-space: pre-line;
}
.en {
font-size: 17px;
color: var(--muted);
white-space: pre-line;
}
/* ===== Tags ===== */
.tags {
position: absolute;
top: 10px;
right: 10px;
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 6px;
max-width: 45%;
}
.chip {
display: inline-block;
padding: 4px 10px;
border: 1px solid var(--border);
border-radius: 999px;
background: var(--chip-bg);
font-size: 12px;
color: var(--muted);
}
/* ===== Images:并排 + 自动换行 ===== */
.imgwrap {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 12px;
}
.imgwrap img {
display: block;
width: calc(48% - 6px);
height: auto;
border: 1px solid var(--border);
border-radius: 14px;
object-fit: cover;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
}
.imgwrap img:hover {
transform: translateY(-2px);
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.12);
}
/* ===== Tips ===== */
.tips {
position: relative;
margin-top: 12px;
padding: 12px 14px;
border: 1px solid var(--border);
border-radius: 12px;
background: color-mix(in srgb, var(--chip-bg) 65%, transparent);
font-size: 15px;
line-height: 1.6;
}
.tips::before {
content: "";
position: absolute;
top: 10px;
bottom: 10px;
left: 0;
width: 3px;
border-radius: 2px;
background: var(--accent);
opacity: 0.7;
}
.tips-title {
margin-bottom: 6px;
padding-left: 6px;
font-size: 14px;
font-weight: 700;
color: var(--accent);
}
.tips-content {
padding-left: 6px;
white-space: pre-line;
}
/* ===== Example ===== */
.example {
padding: 10px 12px;
border-left: 3px solid var(--accent);
border-radius: 10px;
background: color-mix(in srgb, var(--chip-bg) 70%, transparent);
font-size: 17px;
}
/* ===== Morph ===== */
.morph {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
font-size: 14px;
color: var(--muted);
}
.morph .box {
padding: 10px;
border: 1px solid var(--border);
border-radius: 12px;
background: color-mix(in srgb, var(--panel) 80%, transparent);
}
.morph b {
color: var(--fg);
}
/* ===== Responsive ===== */
@media (max-width: 600px) {
.word {
font-size: 34px;
}
.morph {
grid-template-columns: 1fr;
}
}
/* ===== Form Fix ===== */
input,
textarea,
select {
padding: 8px 10px;
border: 1px solid var(--border);
border-radius: 10px;
outline: none;
background: color-mix(in srgb, var(--panel) 88%, transparent);
color: var(--fg);
}
input,
textarea {
caret-color: var(--accent);
}
input::placeholder,
textarea::placeholder {
color: color-mix(in srgb, var(--muted) 75%, transparent);
}
input:focus,
textarea:focus,
select:focus {
border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}