d35211071a
- standardize border-radius from 8px to 6px for consistent styling - adjust padding values from 1rem to 0.75rem for better visual hierarchy - remove redundant overflow-y properties for cleaner code
761 lines
18 KiB
Vue
761 lines
18 KiB
Vue
<!--
|
||
BundlerComparisonDemo.vue
|
||
打包工具对比演示 (Vite/Webpack/Rollup)
|
||
|
||
用途:
|
||
直观对比三大主流打包工具的差异和适用场景。
|
||
|
||
交互功能:
|
||
- 工具切换:对比 Vite、Webpack、Rollup
|
||
- 维度对比:构建速度、配置复杂度、生态丰富度等
|
||
- 场景推荐:根据项目类型推荐最适合的工具
|
||
-->
|
||
<template>
|
||
<div class="bundler-comparison-demo">
|
||
<div class="control-panel">
|
||
<div class="title-section">
|
||
<span class="icon">⚖️</span>
|
||
<span class="title">打包工具对比</span>
|
||
<span class="subtitle">Vite vs Webpack vs Rollup</span>
|
||
</div>
|
||
<div class="view-controls">
|
||
<button
|
||
v-for="view in viewModes"
|
||
:key="view.id"
|
||
class="view-btn"
|
||
:class="{ active: currentView === view.id }"
|
||
@click="currentView = view.id"
|
||
>
|
||
{{ view.icon }} {{ view.name }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 雷达图对比视图 -->
|
||
<div v-if="currentView === 'radar'" class="radar-view">
|
||
<div class="radar-container">
|
||
<svg viewBox="0 0 400 400" class="radar-chart">
|
||
<!-- 背景网格 -->
|
||
<g class="grid">
|
||
<polygon
|
||
v-for="i in 5"
|
||
:key="i"
|
||
:points="getGridPoints(i * 20)"
|
||
fill="none"
|
||
stroke="var(--vp-c-divider)"
|
||
stroke-width="1"
|
||
/>
|
||
<!-- 轴线 -->
|
||
<line
|
||
v-for="(dim, i) in dimensions"
|
||
:key="i"
|
||
:x1="200"
|
||
:y1="200"
|
||
:x2="getAxisEnd(i).x"
|
||
:y2="getAxisEnd(i).y"
|
||
stroke="var(--vp-c-divider)"
|
||
stroke-width="1"
|
||
/>
|
||
</g>
|
||
|
||
<!-- 数据区域 -->
|
||
<g class="data-areas">
|
||
<polygon
|
||
v-for="(tool, toolIndex) in bundlers"
|
||
:key="tool.id"
|
||
:points="getDataPoints(tool.scores)"
|
||
:fill="tool.color"
|
||
:stroke="tool.borderColor"
|
||
fill-opacity="0.2"
|
||
stroke-width="2"
|
||
class="data-polygon"
|
||
:class="{ dimmed: highlightedTool && highlightedTool !== tool.id }"
|
||
@mouseenter="highlightedTool = tool.id"
|
||
@mouseleave="highlightedTool = null"
|
||
/>
|
||
</g>
|
||
|
||
<!-- 维度标签 -->
|
||
<g class="labels">
|
||
<text
|
||
v-for="(dim, i) in dimensions"
|
||
:key="i"
|
||
:x="getLabelPos(i).x"
|
||
:y="getLabelPos(i).y"
|
||
text-anchor="middle"
|
||
dominant-baseline="middle"
|
||
fill="var(--vp-c-text-1)"
|
||
font-size="12"
|
||
font-weight="bold"
|
||
>
|
||
{{ dim.name }}
|
||
</text>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
|
||
<!-- 图例 -->
|
||
<div class="legend">
|
||
<div
|
||
v-for="tool in bundlers"
|
||
:key="tool.id"
|
||
class="legend-item"
|
||
:class="{ dimmed: highlightedTool && highlightedTool !== tool.id }"
|
||
@mouseenter="highlightedTool = tool.id"
|
||
@mouseleave="highlightedTool = null"
|
||
>
|
||
<span class="legend-color" :style="{ background: tool.borderColor }"></span>
|
||
<span class="legend-name">{{ tool.name }}</span>
|
||
<span class="legend-desc">{{ tool.shortDesc }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 表格对比视图 -->
|
||
<div v-else-if="currentView === 'table'" class="table-view">
|
||
<table class="comparison-table">
|
||
<thead>
|
||
<tr>
|
||
<th>对比维度</th>
|
||
<th v-for="tool in bundlers" :key="tool.id">
|
||
<span class="tool-header">
|
||
<span class="tool-icon" :style="{ background: tool.borderColor }">{{ tool.icon }}</span>
|
||
{{ tool.name }}
|
||
</span>
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="(dim, dimIndex) in dimensions" :key="dim.key">
|
||
<td class="dim-name">
|
||
<span class="dim-icon">{{ dim.icon }}</span>
|
||
{{ dim.name }}
|
||
</td>
|
||
<td
|
||
v-for="tool in bundlers"
|
||
:key="tool.id"
|
||
class="score-cell"
|
||
>
|
||
<div class="score-bar-wrapper">
|
||
<div
|
||
class="score-bar"
|
||
:style="{
|
||
width: `${tool.scores[dimIndex] * 10}%`,
|
||
background: tool.borderColor
|
||
}"
|
||
></div>
|
||
<span class="score-value">{{ tool.scores[dimIndex] }}/10</span>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 场景推荐视图 -->
|
||
<div v-else-if="currentView === 'recommend'" class="recommend-view">
|
||
<div class="scenario-list">
|
||
<div
|
||
v-for="scenario in scenarios"
|
||
:key="scenario.id"
|
||
class="scenario-card"
|
||
:class="{ expanded: expandedScenario === scenario.id }"
|
||
>
|
||
<div
|
||
class="scenario-header"
|
||
@click="toggleScenario(scenario.id)"
|
||
>
|
||
<span class="scenario-icon">{{ scenario.icon }}</span>
|
||
<div class="scenario-title-wrap">
|
||
<span class="scenario-name">{{ scenario.name }}</span>
|
||
<span class="scenario-desc">{{ scenario.shortDesc }}</span>
|
||
</div>
|
||
<span class="expand-icon">{{ expandedScenario === scenario.id ? '▼' : '▶' }}</span>
|
||
</div>
|
||
|
||
<div v-if="expandedScenario === scenario.id" class="scenario-content">
|
||
<div class="recommendation">
|
||
<div class="best-choice">
|
||
<span class="choice-label">🏆 首选推荐</span>
|
||
<div class="choice-content">
|
||
<span
|
||
class="tool-badge"
|
||
:style="{ background: getTool(scenario.bestChoice).borderColor }"
|
||
>
|
||
{{ getTool(scenario.bestChoice).icon }} {{ getTool(scenario.bestChoice).name }}
|
||
</span>
|
||
<p class="choice-reason">{{ scenario.bestReason }}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="scenario.alternative" class="alternative">
|
||
<span class="choice-label">🥈 备选方案</span>
|
||
<div class="choice-content">
|
||
<span
|
||
class="tool-badge alt"
|
||
:style="{ background: getTool(scenario.alternative).borderColor }"
|
||
>
|
||
{{ getTool(scenario.alternative).icon }} {{ getTool(scenario.alternative).name }}
|
||
</span>
|
||
<p class="choice-reason">{{ scenario.altReason }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info-box">
|
||
<p>
|
||
<span class="icon">💡</span>
|
||
<strong>选择建议:</strong>
|
||
{{ currentView === 'radar' ? '雷达图展示了各工具在多个维度的能力分布,面积越大代表综合能力越强。' :
|
||
currentView === 'table' ? '表格详细对比了各工具在每个维度的具体得分,方便精确对比。' :
|
||
'根据你的项目类型和团队情况,选择最适合的工具往往比选择"最好"的工具更重要。' }}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
|
||
const currentView = ref('radar')
|
||
const highlightedTool = ref(null)
|
||
const expandedScenario = ref(null)
|
||
|
||
const viewModes = [
|
||
{ id: 'radar', name: '雷达图', icon: '📊' },
|
||
{ id: 'table', name: '对比表', icon: '📋' },
|
||
{ id: 'recommend', name: '场景推荐', icon: '🎯' }
|
||
]
|
||
|
||
const dimensions = [
|
||
{ key: 'speed', name: '构建速度', icon: '⚡' },
|
||
{ key: 'config', name: '配置难度', icon: '🔧' },
|
||
{ key: 'ecosystem', name: '生态丰富', icon: '📦' },
|
||
{ key: 'hmr', name: '热更新速度', icon: '🔥' },
|
||
{ key: 'output', name: '产物优化', icon: '✨' },
|
||
{ key: 'memory', name: '内存占用', icon: '💾' }
|
||
]
|
||
|
||
const bundlers = [
|
||
{
|
||
id: 'vite',
|
||
name: 'Vite',
|
||
icon: '⚡',
|
||
shortDesc: '下一代前端构建工具',
|
||
color: 'rgba(100, 108, 255, 0.3)',
|
||
borderColor: '#646cff',
|
||
scores: [10, 8, 7, 10, 8, 9],
|
||
features: ['原生 ESM', '极速 HMR', '基于 esbuild']
|
||
},
|
||
{
|
||
id: 'webpack',
|
||
name: 'Webpack',
|
||
icon: '📦',
|
||
shortDesc: '老牌强大的打包工具',
|
||
color: 'rgba(142, 214, 251, 0.3)',
|
||
borderColor: '#8ed6fb',
|
||
scores: [5, 5, 10, 6, 9, 5],
|
||
features: ['生态最丰富', 'loader/plugin 多', '配置灵活']
|
||
},
|
||
{
|
||
id: 'rollup',
|
||
name: 'Rollup',
|
||
icon: '📜',
|
||
shortDesc: 'JavaScript 模块打包器',
|
||
color: 'rgba(255, 107, 107, 0.3)',
|
||
borderColor: '#ff6b6b',
|
||
scores: [7, 7, 6, 7, 10, 8],
|
||
features: ['Tree Shaking', '输出最优', '适合库开发']
|
||
}
|
||
]
|
||
|
||
const scenarios = [
|
||
{
|
||
id: 'spa',
|
||
icon: '🚀',
|
||
name: '中小型 SPA 项目',
|
||
shortDesc: '单页应用,快速开发',
|
||
bestChoice: 'vite',
|
||
bestReason: 'Vite 的极速冷启动和热更新让开发体验极佳,配置简单,是中小型项目的首选。',
|
||
alternative: 'webpack',
|
||
altReason: '如果需要大量自定义配置或依赖特定的 webpack loader,webpack 仍然是可靠的选择。'
|
||
},
|
||
{
|
||
id: 'library',
|
||
icon: '📚',
|
||
name: 'JavaScript 库/组件库',
|
||
shortDesc: '打包发布 npm 包',
|
||
bestChoice: 'rollup',
|
||
bestReason: 'Rollup 生成的代码最干净,Tree Shaking 效果最好,非常适合打包 JavaScript 库。',
|
||
alternative: 'vite',
|
||
altReason: 'Vite 使用 Rollup 进行生产构建,同时提供更好的开发体验,也是现代库开发的好选择。'
|
||
},
|
||
{
|
||
id: 'enterprise',
|
||
icon: '🏢',
|
||
name: '大型企业级应用',
|
||
shortDesc: '复杂业务,多人协作',
|
||
bestChoice: 'webpack',
|
||
bestReason: 'Webpack 生态最成熟,loader 和 plugin 最丰富,能应对各种复杂场景和定制化需求。',
|
||
alternative: 'vite',
|
||
altReason: '如果团队追求更好的开发体验,且项目不需要太多自定义构建逻辑,Vite 也是值得考虑的选项。'
|
||
},
|
||
{
|
||
id: 'ssg',
|
||
icon: '📝',
|
||
name: '静态站点生成 (SSG)',
|
||
shortDesc: '文档站、博客、营销页',
|
||
bestChoice: 'vite',
|
||
bestReason: 'VitePress、Astro 等现代 SSG 工具都基于 Vite,开发体验好,构建速度快。',
|
||
alternative: 'rollup',
|
||
altReason: '一些轻量级 SSG 工具直接使用 Rollup,如果对产物体积要求极高可以考虑。'
|
||
}
|
||
]
|
||
|
||
// 雷达图计算
|
||
const getGridPoints = (radius) => {
|
||
const points = []
|
||
for (let i = 0; i < 6; i++) {
|
||
const angle = (i * 60 - 90) * Math.PI / 180
|
||
const x = 200 + radius * Math.cos(angle)
|
||
const y = 200 + radius * Math.sin(angle)
|
||
points.push(`${x},${y}`)
|
||
}
|
||
return points.join(' ')
|
||
}
|
||
|
||
const getAxisEnd = (index) => {
|
||
const angle = (index * 60 - 90) * Math.PI / 180
|
||
return {
|
||
x: 200 + 100 * Math.cos(angle),
|
||
y: 200 + 100 * Math.sin(angle)
|
||
}
|
||
}
|
||
|
||
const getLabelPos = (index) => {
|
||
const angle = (index * 60 - 90) * Math.PI / 180
|
||
return {
|
||
x: 200 + 125 * Math.cos(angle),
|
||
y: 200 + 125 * Math.sin(angle)
|
||
}
|
||
}
|
||
|
||
const getDataPoints = (scores) => {
|
||
const points = []
|
||
for (let i = 0; i < scores.length; i++) {
|
||
const angle = (i * 60 - 90) * Math.PI / 180
|
||
const radius = scores[i] * 10
|
||
const x = 200 + radius * Math.cos(angle)
|
||
const y = 200 + radius * Math.sin(angle)
|
||
points.push(`${x},${y}`)
|
||
}
|
||
return points.join(' ')
|
||
}
|
||
|
||
const getTool = (id) => bundlers.find(b => b.id === id)
|
||
|
||
const toggleScenario = (id) => {
|
||
expandedScenario.value = expandedScenario.value === id ? null : id
|
||
}
|
||
|
||
const togglePlay = () => {
|
||
// Placeholder for play functionality in this component
|
||
}
|
||
|
||
const reset = () => {
|
||
// Placeholder for reset functionality
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.bundler-comparison-demo {
|
||
border: 1px solid var(--vp-c-divider);
|
||
border-radius: 6px;
|
||
background-color: var(--vp-c-bg-soft);
|
||
padding: 0.75rem;
|
||
margin: 0.5rem 0;
|
||
font-family: var(--vp-font-family-mono);
|
||
}
|
||
|
||
.control-panel {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 1rem;
|
||
background: var(--vp-c-bg);
|
||
padding: 0.75rem;
|
||
border-radius: 6px;
|
||
border: 1px solid var(--vp-c-divider);
|
||
gap: 1rem;
|
||
}
|
||
|
||
.title-section {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.title-section .icon {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.title-section .title {
|
||
font-weight: bold;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.title-section .subtitle {
|
||
color: var(--vp-c-text-2);
|
||
font-size: 0.85rem;
|
||
margin-left: 0.5rem;
|
||
}
|
||
|
||
.view-controls {
|
||
display: flex;
|
||
gap: 0.25rem;
|
||
}
|
||
|
||
.view-btn {
|
||
padding: 0.35rem 0.75rem;
|
||
border-radius: 4px;
|
||
background: var(--vp-c-bg-soft);
|
||
border: 1px solid var(--vp-c-divider);
|
||
color: var(--vp-c-text-2);
|
||
font-size: 0.85rem;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.view-btn:hover {
|
||
background: var(--vp-c-bg-alt);
|
||
}
|
||
|
||
.view-btn.active {
|
||
background: var(--vp-c-brand);
|
||
color: white;
|
||
border-color: var(--vp-c-brand);
|
||
}
|
||
|
||
/* 雷达图视图 */
|
||
.radar-view {
|
||
display: grid;
|
||
grid-template-columns: 1fr 280px;
|
||
gap: 1rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.radar-container {
|
||
background: var(--vp-c-bg);
|
||
border-radius: 6px;
|
||
padding: 0.75rem;
|
||
border: 1px solid var(--vp-c-divider);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.radar-chart {
|
||
width: 100%;
|
||
max-width: 350px;
|
||
height: auto;
|
||
}
|
||
|
||
.data-polygon {
|
||
transition: all 0.3s ease;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.data-polygon:hover {
|
||
fill-opacity: 0.4;
|
||
}
|
||
|
||
.data-polygon.dimmed {
|
||
fill-opacity: 0.1;
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.legend {
|
||
background: var(--vp-c-bg);
|
||
border-radius: 6px;
|
||
padding: 0.75rem;
|
||
border: 1px solid var(--vp-c-divider);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.legend-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
padding: 0.5rem;
|
||
border-radius: 6px;
|
||
transition: all 0.2s;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.legend-item:hover {
|
||
background: var(--vp-c-bg-soft);
|
||
}
|
||
|
||
.legend-item.dimmed {
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.legend-color {
|
||
width: 16px;
|
||
height: 16px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.legend-name {
|
||
font-weight: bold;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.legend-desc {
|
||
font-size: 0.75rem;
|
||
color: var(--vp-c-text-2);
|
||
margin-left: auto;
|
||
}
|
||
|
||
/* 表格视图 */
|
||
.table-view {
|
||
margin-bottom: 1rem;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.comparison-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
background: var(--vp-c-bg);
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--vp-c-divider);
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.comparison-table th,
|
||
.comparison-table td {
|
||
padding: 0.75rem;
|
||
text-align: left;
|
||
border-bottom: 1px solid var(--vp-c-divider);
|
||
}
|
||
|
||
.comparison-table th {
|
||
background: var(--vp-c-bg-soft);
|
||
font-weight: bold;
|
||
}
|
||
|
||
.comparison-table tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.tool-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.tool-icon {
|
||
width: 24px;
|
||
height: 24px;
|
||
border-radius: 4px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.dim-name {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.3rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.score-cell {
|
||
min-width: 120px;
|
||
}
|
||
|
||
.score-bar-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.score-bar {
|
||
height: 8px;
|
||
border-radius: 4px;
|
||
min-width: 20px;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.score-value {
|
||
font-size: 0.75rem;
|
||
color: var(--vp-c-text-2);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* 推荐视图 */
|
||
.recommend-view {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.scenario-list {
|
||
display: grid;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.scenario-card {
|
||
background: var(--vp-c-bg);
|
||
border-radius: 6px;
|
||
border: 1px solid var(--vp-c-divider);
|
||
overflow: hidden;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.scenario-card:hover {
|
||
border-color: var(--vp-c-brand);
|
||
}
|
||
|
||
.scenario-card.expanded {
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.scenario-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
padding: 0.75rem 1rem;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.scenario-header:hover {
|
||
background: var(--vp-c-bg-soft);
|
||
}
|
||
|
||
.scenario-icon {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.scenario-title-wrap {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.scenario-name {
|
||
font-weight: bold;
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.scenario-desc {
|
||
font-size: 0.8rem;
|
||
color: var(--vp-c-text-2);
|
||
}
|
||
|
||
.expand-icon {
|
||
color: var(--vp-c-text-3);
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.scenario-content {
|
||
padding: 0 1rem 1rem;
|
||
border-top: 1px solid var(--vp-c-divider);
|
||
background: var(--vp-c-bg-soft);
|
||
}
|
||
|
||
.recommendation {
|
||
display: grid;
|
||
gap: 0.75rem;
|
||
margin-top: 0.75rem;
|
||
}
|
||
|
||
.best-choice,
|
||
.alternative {
|
||
display: grid;
|
||
grid-template-columns: 100px 1fr;
|
||
gap: 0.75rem;
|
||
align-items: start;
|
||
padding: 0.75rem;
|
||
background: var(--vp-c-bg);
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.choice-label {
|
||
font-size: 0.75rem;
|
||
font-weight: bold;
|
||
color: var(--vp-c-text-2);
|
||
padding-top: 0.3rem;
|
||
}
|
||
|
||
.choice-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.3rem;
|
||
}
|
||
|
||
.tool-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.3rem;
|
||
padding: 0.3rem 0.6rem;
|
||
border-radius: 4px;
|
||
color: white;
|
||
font-weight: bold;
|
||
font-size: 0.85rem;
|
||
width: fit-content;
|
||
}
|
||
|
||
.tool-badge.alt {
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.choice-reason {
|
||
font-size: 0.8rem;
|
||
color: var(--vp-c-text-2);
|
||
margin: 0;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.info-box {
|
||
background-color: var(--vp-c-bg-alt);
|
||
padding: 0.75rem;
|
||
border-radius: 6px;
|
||
font-size: 0.85rem;
|
||
line-height: 1.4;
|
||
color: var(--vp-c-text-2);
|
||
}
|
||
|
||
.info-box .icon {
|
||
margin-right: 0.5rem;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.radar-view {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.control-panel {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.comparison-table {
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.comparison-table th,
|
||
.comparison-table td {
|
||
padding: 0.5rem;
|
||
}
|
||
}
|
||
</style>
|