mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 18:01:09 +02:00
fix(picture): migrate to Astro 5.x content collections and fix TypeScript errors
- Rename dynamic routes from [slug].astro to [...slug].astro for multi-segment paths - Replace deprecated entry.slug with entry.id across all components and utils - Fix TypeScript implicit any types in TemplateFilters and prompt-templates - Add proper type narrowing for feature.note in pricing page - Remove unused marked import from FAQCard - Delete invalid placeholder content files - Add shared-landing-ui dependency and integrate StepsSection/PricingSection - Update tailwind config with shared-landing-ui content paths - Add global.css with Indigo/Violet dark theme variables 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6537863696
commit
36b85fc8a0
29 changed files with 248 additions and 60 deletions
|
|
@ -16,6 +16,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.0",
|
||||
"@manacore/shared-landing-ui": "workspace:*",
|
||||
"@picture/design-tokens": "workspace:*",
|
||||
"astro": "^5.16.0",
|
||||
"astro-i18next": "1.0.0-beta.21",
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ const primaryMetric = data.metrics[0] || null;
|
|||
{
|
||||
!featured && data.metrics.length > 0 && (
|
||||
<div class="mb-4 grid grid-cols-3 gap-2">
|
||||
{data.metrics.slice(0, 3).map((metric) => (
|
||||
{data.metrics.slice(0, 3).map((metric: { value: string; label: string }) => (
|
||||
<div class="rounded-lg bg-gray-50 p-2 text-center dark:bg-gray-700">
|
||||
<div class="text-sm font-bold text-blue-600">{metric.value}</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">{metric.label}</div>
|
||||
|
|
@ -117,7 +117,7 @@ const primaryMetric = data.metrics[0] || null;
|
|||
{
|
||||
data.tags.length > 0 && (
|
||||
<div class="mb-4 flex flex-wrap gap-2">
|
||||
{data.tags.slice(0, 3).map((tag) => (
|
||||
{data.tags.slice(0, 3).map((tag: string) => (
|
||||
<span class="rounded-md bg-gray-100 px-2 py-1 text-xs text-gray-700 dark:bg-gray-700 dark:text-gray-300">
|
||||
{tag}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ const isRecent = isRecentRelease(data.releaseDate);
|
|||
)}
|
||||
</div>
|
||||
|
||||
<a href={`/changelog/${data.slug}`} class="entry-title-link">
|
||||
<a href={`/changelog/${entry.id}`} class="entry-title-link">
|
||||
<h3 class="entry-title">{data.title}</h3>
|
||||
</a>
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ const isRecent = isRecentRelease(data.releaseDate);
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<a href={`/changelog/${data.slug}`} class="read-more-btn">
|
||||
<a href={`/changelog/${entry.id}`} class="read-more-btn">
|
||||
Read More
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const { data } = comparison;
|
|||
---
|
||||
|
||||
<a
|
||||
href={`/comparisons/${data.slug}`}
|
||||
href={`/comparisons/${comparison.id}`}
|
||||
class="comparison-card group"
|
||||
>
|
||||
<div class="card-content">
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const comparisonSchema = {
|
|||
},
|
||||
mainEntityOfPage: {
|
||||
'@type': 'WebPage',
|
||||
'@id': `https://picture.com/comparisons/${data.slug}`,
|
||||
'@id': `https://picture.com/comparisons/${comparison.id}`,
|
||||
},
|
||||
...(data.coverImage && {
|
||||
image: {
|
||||
|
|
@ -90,7 +90,7 @@ const breadcrumbSchema = {
|
|||
'@type': 'ListItem',
|
||||
position: 3,
|
||||
name: data.title,
|
||||
item: `https://picture.com/comparisons/${data.slug}`,
|
||||
item: `https://picture.com/comparisons/${comparison.id}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
import { marked } from 'marked';
|
||||
|
||||
interface Props {
|
||||
faq: CollectionEntry<'faq'>;
|
||||
|
|
@ -42,7 +41,7 @@ const { Content } = await faq.render();
|
|||
}
|
||||
|
||||
.faq-item:hover {
|
||||
@apply border-dark-hover;
|
||||
@apply border-border-hover;
|
||||
}
|
||||
|
||||
.faq-question {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ import { formatCategoryName } from '../../utils/promptTemplates';
|
|||
{
|
||||
difficulties.map((diff) => (
|
||||
<option value={diff}>
|
||||
{diff.charAt(0).toUpperCase() + diff.slice(1)} ({stats.byDifficulty[diff]})
|
||||
{diff.charAt(0).toUpperCase() + diff.slice(1)} ({stats.byDifficulty[diff as keyof typeof stats.byDifficulty]})
|
||||
</option>
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const difficultyColor = getDifficultyColor(data.difficulty);
|
|||
---
|
||||
|
||||
<a
|
||||
href={`/tutorials/${data.slug}`}
|
||||
href={`/tutorials/${tutorial.id}`}
|
||||
class="tutorial-card group"
|
||||
data-difficulty={data.difficulty}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const { data } = useCase;
|
|||
---
|
||||
|
||||
<a
|
||||
href={`/use-cases/${data.slug}`}
|
||||
href={`/use-cases/${useCase.id}`}
|
||||
class="use-case-card group"
|
||||
>
|
||||
<div class="card-content">
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: "Picture vs Traditional Design"
|
||||
order: 1
|
||||
---
|
||||
|
||||
Compare Picture's AI-powered approach with traditional design workflows and see how you can save time and resources.
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: "What is Picture?"
|
||||
order: 1
|
||||
---
|
||||
|
||||
Picture is an AI-powered image generation platform that helps you create stunning visuals from text descriptions.
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
title: "Marketing & Social Media"
|
||||
icon: "📱"
|
||||
order: 1
|
||||
---
|
||||
|
||||
Create eye-catching visuals for your social media campaigns and marketing materials with AI-generated images.
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
// import { HeadHrefLangs } from 'astro-i18next/components';
|
||||
import { t } from '../i18n';
|
||||
import LanguageSwitcher from '@components/LanguageSwitcher.astro';
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import BlogCard from '@components/blog/BlogCard.astro';
|
|||
export async function getStaticPaths() {
|
||||
const allPosts = await getCollection('blog');
|
||||
return allPosts.map(post => ({
|
||||
params: { slug: post.slug },
|
||||
params: { slug: post.id },
|
||||
props: { post },
|
||||
}));
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ import {
|
|||
export async function getStaticPaths() {
|
||||
const changelog = await getCollection('changelog');
|
||||
return changelog.map((entry) => ({
|
||||
params: { slug: entry.slug },
|
||||
params: { slug: entry.id },
|
||||
props: { entry },
|
||||
}));
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ const groupedByYearMonth = await getChangelogGroupedByYearMonth(language);
|
|||
<p class="text-gray-300 text-lg">{latestRelease.data.summary}</p>
|
||||
</div>
|
||||
<a
|
||||
href={`/changelog/${latestRelease.data.slug}`}
|
||||
href={`/changelog/${latestRelease.id}`}
|
||||
class="px-6 py-3 bg-primary text-white font-medium rounded-lg hover:bg-primary/90 transition whitespace-nowrap"
|
||||
>
|
||||
Read Full Release
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { getWinnerBadgeColor, getWinnerBadgeText, calculateOverallWinner } from
|
|||
export async function getStaticPaths() {
|
||||
const comparisons = await getCollection('comparisons');
|
||||
return comparisons.map((comparison) => ({
|
||||
params: { slug: comparison.data.slug },
|
||||
params: { slug: comparison.id },
|
||||
props: { comparison },
|
||||
}));
|
||||
}
|
||||
|
|
@ -28,8 +28,8 @@ const overallWinner = data.winnerBadge || calculateOverallWinner(data.comparison
|
|||
const allComparisons = await getCollection('comparisons');
|
||||
const relatedComparisons = allComparisons
|
||||
.filter((c) =>
|
||||
data.relatedComparisons.includes(c.data.slug) &&
|
||||
c.data.slug !== data.slug &&
|
||||
data.relatedComparisons.includes(c.id) &&
|
||||
c.id !== comparison.id &&
|
||||
c.data.language === data.language
|
||||
)
|
||||
.slice(0, 3);
|
||||
|
|
@ -9,14 +9,10 @@ import FeatureCard from '@components/features/FeatureCard.astro';
|
|||
|
||||
export async function getStaticPaths() {
|
||||
const allFeatures = await getCollection('features');
|
||||
return allFeatures.map(feature => {
|
||||
// Remove language prefix from slug (e.g., "en/cross-platform-apps" -> "cross-platform-apps")
|
||||
const slug = feature.slug.split('/').pop() || feature.slug;
|
||||
return {
|
||||
params: { slug },
|
||||
props: { feature },
|
||||
};
|
||||
});
|
||||
return allFeatures.map(feature => ({
|
||||
params: { slug: feature.id },
|
||||
props: { feature },
|
||||
}));
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
|
@ -5,6 +5,92 @@ import Features from '@components/Features.astro';
|
|||
import Testimonials from '@components/Testimonials.astro';
|
||||
import CTA from '@components/CTA.astro';
|
||||
import Footer from '@components/Footer.astro';
|
||||
|
||||
// Shared components
|
||||
import StepsSection from '@manacore/shared-landing-ui/sections/StepsSection.astro';
|
||||
import PricingSection from '@manacore/shared-landing-ui/sections/PricingSection.astro';
|
||||
|
||||
// Steps data
|
||||
const steps = [
|
||||
{
|
||||
number: '1',
|
||||
title: 'Describe Your Vision',
|
||||
description: 'Enter a text prompt describing the image you want to create. Be as detailed or simple as you like.',
|
||||
image: '/screenshots/prompt.png'
|
||||
},
|
||||
{
|
||||
number: '2',
|
||||
title: 'Choose Your Model',
|
||||
description: 'Select from multiple AI models - FLUX Schnell for speed, Dev for balance, or Pro for maximum quality.',
|
||||
image: '/screenshots/model.png'
|
||||
},
|
||||
{
|
||||
number: '3',
|
||||
title: 'Generate & Download',
|
||||
description: 'Watch as your image is created in seconds. Download in high resolution or save to your gallery.',
|
||||
image: '/screenshots/download.png'
|
||||
}
|
||||
];
|
||||
|
||||
// Pricing preview data
|
||||
const pricingPlans = [
|
||||
{
|
||||
name: 'Free',
|
||||
price: '0',
|
||||
period: '/month',
|
||||
description: 'Perfect for trying out Picture',
|
||||
features: [
|
||||
{ text: '100 images/month', included: true },
|
||||
{ text: 'FLUX Schnell model', included: true },
|
||||
{ text: 'Basic editing tools', included: true },
|
||||
{ text: 'Gallery storage (30 days)', included: true },
|
||||
{ text: 'Pro models', included: false },
|
||||
{ text: 'Priority generation', included: false }
|
||||
],
|
||||
cta: {
|
||||
text: 'Get Started Free',
|
||||
href: '#'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Pro',
|
||||
price: '19',
|
||||
period: '/month',
|
||||
description: 'For professionals & creators',
|
||||
features: [
|
||||
{ text: '2,000 images/month', included: true },
|
||||
{ text: 'All AI models', included: true },
|
||||
{ text: 'Advanced editing', included: true },
|
||||
{ text: 'Unlimited storage', included: true },
|
||||
{ text: 'Priority queue', included: true },
|
||||
{ text: 'Commercial usage', included: true }
|
||||
],
|
||||
cta: {
|
||||
text: 'Start Free Trial',
|
||||
href: '#'
|
||||
},
|
||||
highlighted: true,
|
||||
badge: 'Most Popular'
|
||||
},
|
||||
{
|
||||
name: 'Enterprise',
|
||||
price: 'Custom',
|
||||
period: '',
|
||||
description: 'For teams & businesses',
|
||||
features: [
|
||||
{ text: 'Unlimited images', included: true },
|
||||
{ text: 'All models + early access', included: true },
|
||||
{ text: 'Custom fine-tuning', included: true },
|
||||
{ text: 'API access', included: true },
|
||||
{ text: 'Team collaboration', included: true },
|
||||
{ text: 'SLA guarantee', included: true }
|
||||
],
|
||||
cta: {
|
||||
text: 'Contact Sales',
|
||||
href: '#'
|
||||
}
|
||||
}
|
||||
];
|
||||
---
|
||||
|
||||
<Layout
|
||||
|
|
@ -13,6 +99,25 @@ import Footer from '@components/Footer.astro';
|
|||
>
|
||||
<Hero />
|
||||
<Features />
|
||||
|
||||
<StepsSection
|
||||
id="how-it-works"
|
||||
title="Create Stunning Images in 3 Steps"
|
||||
subtitle="From text to image in seconds - no design skills required"
|
||||
steps={steps}
|
||||
showImages={false}
|
||||
alternateLayout={true}
|
||||
class="bg-dark-surface"
|
||||
/>
|
||||
|
||||
<PricingSection
|
||||
id="pricing"
|
||||
title="Simple, Transparent Pricing"
|
||||
subtitle="Start for free, upgrade as you grow. No hidden fees."
|
||||
plans={pricingPlans}
|
||||
class="bg-dark-bg"
|
||||
/>
|
||||
|
||||
<Testimonials />
|
||||
<CTA />
|
||||
<Footer />
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ const models = [
|
|||
</span>
|
||||
<span class="feature-text">
|
||||
{feature.text}
|
||||
{feature.note && <span class="feature-note"> ({feature.note})</span>}
|
||||
{'note' in feature && feature.note && <span class="feature-note"> ({feature.note})</span>}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { t } from '../../i18n';
|
|||
export const getStaticPaths = (async () => {
|
||||
const templates = await getCollection('promptTemplates');
|
||||
return templates.map((template) => ({
|
||||
params: { slug: template.slug },
|
||||
params: { slug: template.id },
|
||||
props: { template },
|
||||
}));
|
||||
}) satisfies GetStaticPaths;
|
||||
|
|
@ -120,7 +120,7 @@ const allModels = [
|
|||
{
|
||||
difficulties.map((diff) => (
|
||||
<option value={diff}>
|
||||
{diff.charAt(0).toUpperCase() + diff.slice(1)} ({stats.byDifficulty[diff]})
|
||||
{diff.charAt(0).toUpperCase() + diff.slice(1)} ({stats.byDifficulty[diff as keyof typeof stats.byDifficulty]})
|
||||
</option>
|
||||
))
|
||||
}
|
||||
|
|
@ -180,7 +180,7 @@ const allModels = [
|
|||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{featuredTemplates.map((template) => (
|
||||
<a
|
||||
href={`/prompt-templates/${template.slug}`}
|
||||
href={`/prompt-templates/${template.id}`}
|
||||
class="group bg-white border border-gray-200 rounded-xl overflow-hidden hover:shadow-xl transition-all duration-300 hover:-translate-y-1"
|
||||
>
|
||||
{/* Template Card */}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
export async function getStaticPaths() {
|
||||
const tutorials = await getCollection('tutorials');
|
||||
return tutorials.map((tutorial) => ({
|
||||
params: { slug: tutorial.slug },
|
||||
params: { slug: tutorial.id },
|
||||
props: { tutorial },
|
||||
}));
|
||||
}
|
||||
84
picture/apps/landing/src/styles/global.css
Normal file
84
picture/apps/landing/src/styles/global.css
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* Picture Theme CSS Variables - Indigo/Violet (Dark Theme) */
|
||||
:root {
|
||||
/* Primary colors - Picture Indigo */
|
||||
--color-primary: #818cf8;
|
||||
--color-primary-hover: #6366f1;
|
||||
--color-primary-glow: rgba(129, 140, 248, 0.3);
|
||||
|
||||
/* Text colors (Dark theme) */
|
||||
--color-text-primary: #ffffff;
|
||||
--color-text-secondary: #d1d5db;
|
||||
--color-text-muted: #9ca3af;
|
||||
|
||||
/* Background colors (Dark theme) */
|
||||
--color-background-page: #000000;
|
||||
--color-background-card: #1a1a1a;
|
||||
--color-background-card-hover: #242424;
|
||||
|
||||
/* Border colors */
|
||||
--color-border: #383838;
|
||||
--color-border-hover: #4f4f4f;
|
||||
}
|
||||
|
||||
/* Base styles */
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', system-ui, sans-serif;
|
||||
background-color: var(--color-background-page);
|
||||
color: var(--color-text-primary);
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* Selection */
|
||||
::selection {
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Focus styles */
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--color-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Gradient text */
|
||||
.text-gradient {
|
||||
background: linear-gradient(135deg, #818cf8 0%, #a78bfa 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
/* Animation utilities */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fadeIn {
|
||||
animation: fadeIn 0.6s ease-out forwards;
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.btn-primary {
|
||||
@apply inline-flex items-center justify-center px-6 py-3 text-base font-medium text-white bg-gradient-to-r from-primary-600 to-secondary-600 rounded-lg hover:from-primary-700 hover:to-secondary-700 transition-all duration-300 shadow-lg;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply inline-flex items-center justify-center px-6 py-3 text-base font-medium text-white bg-dark-elevated border border-dark-border rounded-lg hover:border-primary/50 transition-all duration-300;
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ export async function getRelatedPosts(
|
|||
const allPosts = await getBlogPosts(post.data.language);
|
||||
|
||||
// Filter out current post
|
||||
const otherPosts = allPosts.filter(p => p.slug !== post.slug);
|
||||
const otherPosts = allPosts.filter(p => p.id !== post.id);
|
||||
|
||||
// Score posts by relevance (same category, shared tags)
|
||||
const scoredPosts = otherPosts.map(p => {
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ export async function getRelatedCaseStudies(
|
|||
const caseStudies = await getAllCaseStudies();
|
||||
|
||||
// Filter out current case study
|
||||
const others = caseStudies.filter((cs) => cs.data.slug !== currentCaseStudy.data.slug);
|
||||
const others = caseStudies.filter((cs) => cs.id !== currentCaseStudy.id);
|
||||
|
||||
// Score based on similarity
|
||||
const scored = others.map((cs) => {
|
||||
|
|
@ -176,7 +176,7 @@ export async function getRelatedCaseStudies(
|
|||
score += sharedModels.length;
|
||||
|
||||
// Explicit related case studies = +20 points
|
||||
if (currentCaseStudy.data.relatedCaseStudies.includes(cs.data.slug)) {
|
||||
if (currentCaseStudy.data.relatedCaseStudies.includes(cs.id)) {
|
||||
score += 20;
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +215,7 @@ export async function searchCaseStudies(query: string): Promise<CaseStudyEntry[]
|
|||
*/
|
||||
export async function getCaseStudyBySlug(slug: string): Promise<CaseStudyEntry | undefined> {
|
||||
const caseStudies = await getAllCaseStudies();
|
||||
return caseStudies.find((cs) => cs.data.slug === slug);
|
||||
return caseStudies.find((cs) => cs.id === slug || cs.id.replace('en/', '') === slug);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -43,13 +43,13 @@ export async function getRelatedFeatures(
|
|||
|
||||
// Filter out current feature and same category
|
||||
const relatedFeatures = allFeatures
|
||||
.filter(f => f.slug !== feature.slug && f.data.category === feature.data.category)
|
||||
.filter(f => f.id !== feature.id && f.data.category === feature.data.category)
|
||||
.slice(0, limit);
|
||||
|
||||
// If not enough, add from other categories
|
||||
if (relatedFeatures.length < limit) {
|
||||
const remaining = allFeatures
|
||||
.filter(f => f.slug !== feature.slug && !relatedFeatures.includes(f))
|
||||
.filter(f => f.id !== feature.id && !relatedFeatures.includes(f))
|
||||
.slice(0, limit - relatedFeatures.length);
|
||||
relatedFeatures.push(...remaining);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,12 +185,12 @@ export async function getRelatedTutorials(
|
|||
const allTutorials = await getTutorials(tutorial.data.language);
|
||||
|
||||
// Filter out current tutorial
|
||||
const otherTutorials = allTutorials.filter((t) => t.slug !== tutorial.slug);
|
||||
const otherTutorials = allTutorials.filter((t) => t.id !== tutorial.id);
|
||||
|
||||
// Get tutorials from related slugs
|
||||
const relatedSlugs = tutorial.data.relatedTutorials;
|
||||
const relatedBySlug = otherTutorials.filter((t) =>
|
||||
relatedSlugs.includes(t.data.slug)
|
||||
relatedSlugs.includes(t.id)
|
||||
);
|
||||
|
||||
// Get tutorials from same category
|
||||
|
|
|
|||
|
|
@ -3,9 +3,30 @@ import preset from '@picture/design-tokens/tailwind/preset';
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
presets: [preset],
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||
content: [
|
||||
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
|
||||
'../../packages/shared-landing-ui/src/**/*.{astro,html,js,jsx,ts,tsx}'
|
||||
],
|
||||
theme: {
|
||||
extend: {}
|
||||
extend: {
|
||||
colors: {
|
||||
// CSS variable mappings for shared-landing-ui compatibility
|
||||
background: {
|
||||
page: 'var(--color-background-page, #000000)',
|
||||
card: 'var(--color-background-card, #1a1a1a)',
|
||||
'card-hover': 'var(--color-background-card-hover, #242424)'
|
||||
},
|
||||
text: {
|
||||
primary: 'var(--color-text-primary, #ffffff)',
|
||||
secondary: 'var(--color-text-secondary, #d1d5db)',
|
||||
muted: 'var(--color-text-muted, #9ca3af)'
|
||||
},
|
||||
border: {
|
||||
DEFAULT: 'var(--color-border, #383838)',
|
||||
hover: 'var(--color-border-hover, #4f4f4f)'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/typography')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue