与 Tailwind CSS 集成
将 rehype-smart-links 与 Tailwind CSS 集成,为不同类型的链接添加精美的样式。本页面展示了多种使用 Tailwind 实现的链接样式效果。
基础样式
使用 Tailwind 的基础样式为链接添加颜色和交互效果:
基础Tailwind样式
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
internalLinkClass: 'text-blue-600 hover:text-blue-800 underline hover:no-underline font-medium',
externalLinkClass: 'text-purple-600 hover:text-purple-800 underline hover:no-underline font-medium',
brokenLinkClass: 'text-red-500 hover:text-red-700 line-through hover:no-underline font-medium',
externalLinkAttributes: {
target: '_blank',
rel: 'noopener noreferrer'
}
}
]]
}
});
带图标的链接
使用 Tailwind 的 Flex 布局为链接添加图标:
带图标的链接
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
wrapperTemplate: (node, linkType) => {
let icon;
if (linkType === 'internal') {
// 内部链接添加箭头图标
icon = h('svg', {
class: 'w-4 h-4',
fill: 'none',
viewBox: '0 0 24 24',
stroke: 'currentColor'
}, [
h('path', {
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2',
d: 'M9 5l7 7-7 7'
})
]);
// 添加链接样式
node.properties.className = 'inline-flex items-center gap-1 text-blue-600 hover:text-blue-800';
node.children.push(icon);
}
else if (linkType === 'external') {
// 外部链接添加外部链接图标
icon = h('svg', {
class: 'w-4 h-4',
fill: 'none',
viewBox: '0 0 24 24',
stroke: 'currentColor'
}, [
h('path', {
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2',
d: 'M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14'
})
]);
// 添加链接样式和属性
node.properties.className = 'inline-flex items-center gap-1 text-purple-600 hover:text-purple-800';
node.properties.target = '_blank';
node.properties.rel = 'noopener noreferrer';
node.children.push(icon);
}
else if (linkType === 'broken') {
// 断开链接添加警告图标
icon = h('svg', {
class: 'w-4 h-4',
fill: 'none',
viewBox: '0 0 24 24',
stroke: 'currentColor'
}, [
h('path', {
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2',
d: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z'
})
]);
// 添加链接样式并将图标添加到链接开头
node.properties.className = 'inline-flex items-center gap-1 text-red-500 hover:text-red-700';
node.children = [icon, ...node.children];
}
return node;
}
}
]]
}
});
按钮样式链接
使用 Tailwind 创建按钮样式的链接:
按钮样式链接
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
internalLinkClass: 'inline-block px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors duration-200',
externalLinkClass: 'inline-block px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white font-medium rounded-lg transition-colors duration-200',
brokenLinkClass: 'inline-block px-4 py-2 bg-gray-300 hover:bg-gray-400 text-gray-600 font-medium rounded-lg cursor-not-allowed transition-colors duration-200',
externalLinkAttributes: {
target: '_blank',
rel: 'noopener noreferrer'
}
}
]]
}
});
卡片样式链接
创建更复杂的卡片样式链接:
卡片样式链接
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
wrapperTemplate: (node, linkType, url) => {
// 设置链接基础样式
const baseClass = 'block p-6 max-w-sm rounded-lg border shadow-md transition-all duration-200';
let customClass, title, description;
if (linkType === 'internal') {
customClass = 'bg-white border-gray-200 hover:shadow-lg hover:bg-gray-50 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700';
title = '内部文档';
description = '点击访问网站内部文档页面,了解更多详情。';
}
else if (linkType === 'external') {
customClass = 'bg-white border-gray-200 hover:shadow-lg hover:bg-gray-50 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700';
title = 'GitHub 仓库';
description = '点击访问我们的 GitHub 仓库,获取源代码和最新更新。';
// 添加外部链接属性
node.properties.target = '_blank';
node.properties.rel = 'noopener noreferrer';
}
else if (linkType === 'broken') {
customClass = 'bg-red-50 border-red-200 dark:bg-gray-800 dark:border-red-700 cursor-not-allowed';
title = '链接失效';
description = '此链接指向的页面不存在或已被移除。';
}
// 设置完整的类名
node.properties.className = `${baseClass} ${customClass}`;
// 创建标题和描述
const titleEl = h('h5', {
class: 'mb-2 text-2xl font-bold tracking-tight ' +
(linkType === 'broken' ? 'text-red-500 dark:text-red-400' : 'text-gray-900 dark:text-white')
}, title);
const descEl = h('p', {
class: 'font-normal ' +
(linkType === 'broken' ? 'text-red-700 dark:text-red-300' : 'text-gray-700 dark:text-gray-400')
}, description);
// 设置链接内容
node.children = [titleEl, descEl];
return node;
}
}
]]
}
});
带过渡动画的链接
使用 Tailwind 添加过渡效果和动画:
带动画的链接
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
wrapperTemplate: (node, linkType) => {
// 获取原始文本内容
const originalContent = node.children[0]?.value || '链接';
if (linkType === 'internal') {
// 创建带有下划线动画的内部链接
const lineElement = h('span', {
class: 'absolute bottom-0 left-0 w-0 h-0.5 bg-blue-600 group-hover:w-full transition-all duration-300'
});
const textElement = h('span', {
class: 'group-hover:text-blue-800 transition-colors duration-300'
}, '了解更多');
// 设置链接样式和内容
node.properties.className = 'relative inline-block text-blue-600 font-medium group';
node.children = [lineElement, textElement];
}
else if (linkType === 'external') {
// 创建带有下划线动画和箭头的外部链接
const lineElement = h('span', {
class: 'absolute bottom-0 left-0 w-0 h-0.5 bg-purple-600 group-hover:w-full transition-all duration-300'
});
const textElement = h('span', {
class: 'group-hover:text-purple-800 transition-colors duration-300'
}, 'GitHub');
const arrowElement = h('span', {
class: 'inline-block transform translate-x-0 group-hover:translate-x-1 transition-transform duration-200'
}, '→');
// 设置链接样式和内容
node.properties.className = 'relative inline-block text-purple-600 font-medium group';
node.properties.target = '_blank';
node.properties.rel = 'noopener noreferrer';
node.children = [lineElement, textElement, arrowElement];
}
else if (linkType === 'broken') {
// 创建失效链接样式
const lineElement = h('span', {
class: 'absolute bottom-0 left-0 w-full h-0.5 bg-red-500 opacity-50'
});
const textElement = h('span', {
class: 'line-through'
}, '失效链接');
// 设置链接样式和内容
node.properties.className = 'relative inline-block text-red-500 font-medium';
node.children = [lineElement, textElement];
}
return node;
}
}
]]
}
});
自适应暗色模式
Tailwind 的暗色模式支持:
暗色模式适配链接
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
internalLinkClass: 'text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 underline dark:decoration-blue-400 hover:decoration-blue-800 dark:hover:decoration-blue-300 transition-colors duration-200',
externalLinkClass: 'text-purple-600 dark:text-purple-400 hover:text-purple-800 dark:hover:text-purple-300 underline dark:decoration-purple-400 hover:decoration-purple-800 dark:hover:decoration-purple-300 transition-colors duration-200',
brokenLinkClass: 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 line-through dark:decoration-red-400 hover:decoration-red-800 dark:hover:decoration-red-300 transition-colors duration-200',
externalLinkAttributes: {
target: '_blank',
rel: 'noopener noreferrer'
}
}
]]
}
});
徽章样式链接
徽章效果的链接样式:
徽章样式链接
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
wrapperTemplate: (node, linkType) => {
// 获取原始内容
const originalContent = node.children[0]?.value || '链接';
let badgeText, mainClass, badgeClass;
if (linkType === 'internal') {
badgeText = '内部';
mainClass = 'inline-flex items-center px-3 py-1 bg-blue-100 text-blue-800 text-sm font-medium rounded-full hover:bg-blue-200 dark:bg-blue-900 dark:text-blue-300 dark:hover:bg-blue-800 transition-colors duration-200';
badgeClass = 'ml-1 bg-blue-500 text-blue-100 text-xs px-2 py-0.5 rounded-full dark:bg-blue-300 dark:text-blue-900';
node.children = ['文档'];
}
else if (linkType === 'external') {
badgeText = '外部';
mainClass = 'inline-flex items-center px-3 py-1 bg-purple-100 text-purple-800 text-sm font-medium rounded-full hover:bg-purple-200 dark:bg-purple-900 dark:text-purple-300 dark:hover:bg-purple-800 transition-colors duration-200';
badgeClass = 'ml-1 bg-purple-500 text-purple-100 text-xs px-2 py-0.5 rounded-full dark:bg-purple-300 dark:text-purple-900';
node.children = ['GitHub'];
// 添加外部链接属性
node.properties.target = '_blank';
node.properties.rel = 'noopener noreferrer';
}
else if (linkType === 'broken') {
badgeText = '错误';
mainClass = 'inline-flex items-center px-3 py-1 bg-red-100 text-red-800 text-sm font-medium rounded-full hover:bg-red-200 dark:bg-red-900 dark:text-red-300 dark:hover:bg-red-800 transition-colors duration-200';
badgeClass = 'ml-1 bg-red-500 text-red-100 text-xs px-2 py-0.5 rounded-full dark:bg-red-300 dark:text-red-900';
node.children = ['链接失效'];
}
// 创建徽章
const badge = h('span', { class: badgeClass }, badgeText);
// 设置链接属性和内容
node.properties.className = mainClass;
node.children.push(badge);
return node;
}
}
]]
}
});
综合方案
结合多种 Tailwind 样式和 CSS 属性:
综合样式方案
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
wrapperTemplate: (node, linkType) => {
// 创建包装容器
const containerDiv = h('div', {
class: linkType === 'broken' ? 'relative' : 'relative group'
});
let linkClass, iconPath, animationClass;
if (linkType === 'internal') {
linkClass = 'inline-flex items-center py-2 px-4 bg-gradient-to-r from-blue-500 to-blue-700 text-white font-medium rounded-lg hover:from-blue-600 hover:to-blue-800 shadow-md hover:shadow-lg transform hover:-translate-y-0.5 transition-all duration-200';
iconPath = 'M9 5l7 7-7 7';
animationClass = 'absolute -bottom-2 left-0 w-full h-0.5 bg-blue-500 transform origin-left scale-x-0 group-hover:scale-x-100 transition-transform duration-300';
}
else if (linkType === 'external') {
linkClass = 'inline-flex items-center py-2 px-4 bg-gradient-to-r from-purple-500 to-purple-700 text-white font-medium rounded-lg hover:from-purple-600 hover:to-purple-800 shadow-md hover:shadow-lg transform hover:-translate-y-0.5 transition-all duration-200';
iconPath = 'M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14';
animationClass = 'absolute -bottom-2 left-0 w-full h-0.5 bg-purple-500 transform origin-left scale-x-0 group-hover:scale-x-100 transition-transform duration-300';
// 添加外部链接属性
node.properties.target = '_blank';
node.properties.rel = 'noopener noreferrer';
}
else if (linkType === 'broken') {
linkClass = 'inline-flex items-center py-2 px-4 bg-gray-100 dark:bg-gray-700 text-gray-400 font-medium rounded-lg cursor-not-allowed line-through';
iconPath = 'M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z';
}
// 创建图标
const icon = h('svg', {
class: linkType === 'broken' ? 'ml-1 w-4 h-4' : 'ml-1 w-4 h-4 transition-transform group-hover:translate-x-1 duration-200',
fill: 'none',
viewBox: '0 0 24 24',
stroke: 'currentColor'
}, [
h('path', {
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2',
d: iconPath
})
]);
// 设置链接样式和内容
node.properties.className = linkClass;
node.children = [
linkType === 'internal' ? '关于我们' : linkType === 'external' ? 'GitHub' : '链接失效',
icon
];
// 添加动画元素(仅适用于内部和外部链接)
if (linkType !== 'broken') {
const animationLine = h('span', { class: animationClass });
containerDiv.children = [node, animationLine];
} else {
containerDiv.children = [node];
}
return containerDiv;
}
}
]]
}
});
下一步
浏览更多示例: