Browse Source

Merge pull request 'Created a reusable BubbleContainer component with glassmorphism design featuring transparency, rounded borders, light/dark mode support, and multiple variants (default, subtle, prominent) with different padding options. Updated the About page to serve as a component library showcase demonstrating the BubbleContainer's various uses and configurations.' (#4) from ai/1769646695585 into main

Reviewed-on: #4
main
Diego Vester 2 months ago
parent
commit
783c32ca9d
  1. 166
      src/components/ui/BubbleContainer.vue
  2. 316
      src/views/AboutView.vue

166
src/components/ui/BubbleContainer.vue

@ -0,0 +1,166 @@ @@ -0,0 +1,166 @@
<script setup lang="ts">
interface Props {
padding?: 'sm' | 'md' | 'lg' | 'xl'
variant?: 'default' | 'subtle' | 'prominent'
}
withDefaults(defineProps<Props>(), {
padding: 'md',
variant: 'default'
})
</script>
<template>
<div :class="['bubble-container', `bubble-container--${padding}`, `bubble-container--${variant}`]">
<slot />
</div>
</template>
<style scoped>
.bubble-container {
/* Base structure */
position: relative;
border-radius: 1.25rem;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
/* Light mode base */
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.25),
rgba(255, 255, 255, 0.08)
);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
inset 0 -1px 0 rgba(0, 0, 0, 0.05);
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
.bubble-container {
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.12),
rgba(255, 255, 255, 0.03)
);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.15),
inset 0 -1px 0 rgba(0, 0, 0, 0.2);
}
}
/* Padding variants */
.bubble-container--sm {
padding: 0.75rem;
}
.bubble-container--md {
padding: 1.25rem;
}
.bubble-container--lg {
padding: 2rem;
}
.bubble-container--xl {
padding: 3rem;
}
/* Style variants */
.bubble-container--subtle {
/* Light mode subtle */
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.15),
rgba(255, 255, 255, 0.05)
);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow:
0 4px 16px rgba(0, 0, 0, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
@media (prefers-color-scheme: dark) {
.bubble-container--subtle {
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.08),
rgba(255, 255, 255, 0.02)
);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow:
0 4px 16px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
}
.bubble-container--prominent {
/* Light mode prominent */
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.35),
rgba(255, 255, 255, 0.12)
);
border: 1px solid rgba(255, 255, 255, 0.4);
box-shadow:
0 12px 48px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.5),
inset 0 -1px 0 rgba(0, 0, 0, 0.08);
}
@media (prefers-color-scheme: dark) {
.bubble-container--prominent {
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.18),
rgba(255, 255, 255, 0.05)
);
border: 1px solid rgba(255, 255, 255, 0.25);
box-shadow:
0 12px 48px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2),
inset 0 -1px 0 rgba(0, 0, 0, 0.3);
}
}
/* Hover effects */
.bubble-container {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.bubble-container:hover {
transform: translateY(-2px);
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.5),
inset 0 -1px 0 rgba(0, 0, 0, 0.08);
}
@media (prefers-color-scheme: dark) {
.bubble-container:hover {
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2),
inset 0 -1px 0 rgba(0, 0, 0, 0.25);
}
}
/* Responsive adjustments */
@media (max-width: 768px) {
.bubble-container {
border-radius: 1rem;
}
.bubble-container--lg {
padding: 1.5rem;
}
.bubble-container--xl {
padding: 2rem;
}
}
</style>

316
src/views/AboutView.vue

@ -1,15 +1,315 @@ @@ -1,15 +1,315 @@
<script setup lang="ts">
import BubbleContainer from '../components/ui/BubbleContainer.vue'
</script>
<template>
<div class="about">
<h1>This is an about page</h1>
<div class="about-content">
<h1 class="page-title">Component Library</h1>
<p class="page-subtitle">Experimenting with reusable UI components</p>
<div class="component-showcase">
<section class="showcase-section">
<h2>Bubble Container</h2>
<p class="section-description">
A versatile container component with glassmorphism design, featuring transparency,
rounded borders, and automatic light/dark mode support.
</p>
<div class="examples-grid">
<div class="example">
<h3>Default</h3>
<BubbleContainer>
<h4>Standard Container</h4>
<p>This is the default bubble container with medium padding and standard transparency.</p>
<div class="demo-content">
<span class="demo-tag">Feature 1</span>
<span class="demo-tag">Feature 2</span>
</div>
</BubbleContainer>
</div>
<div class="example">
<h3>Subtle Variant</h3>
<BubbleContainer variant="subtle" padding="lg">
<h4>Subtle Style</h4>
<p>Less prominent with reduced opacity and softer shadows for background elements.</p>
<div class="demo-list">
<div class="demo-item"> Reduced opacity</div>
<div class="demo-item"> Softer shadows</div>
<div class="demo-item"> Large padding</div>
</div>
</BubbleContainer>
</div>
<div class="example">
<h3>Prominent Variant</h3>
<BubbleContainer variant="prominent" padding="xl">
<h4>Prominent Style</h4>
<p>Enhanced visibility with stronger borders and more pronounced shadows.</p>
<div class="demo-stats">
<div class="stat">
<div class="stat-number">95%</div>
<div class="stat-label">Transparency</div>
</div>
<div class="stat">
<div class="stat-number">10px</div>
<div class="stat-label">Blur Effect</div>
</div>
</div>
</BubbleContainer>
</div>
<div class="example">
<h3>Small Padding</h3>
<BubbleContainer padding="sm">
<h4>Compact Content</h4>
<p>Perfect for smaller UI elements and tight layouts.</p>
</BubbleContainer>
</div>
<div class="example full-width">
<h3>Nested Containers</h3>
<BubbleContainer padding="lg">
<h4>Parent Container</h4>
<p>Containers can be nested for complex layouts:</p>
<div class="nested-demo">
<BubbleContainer variant="subtle" padding="md">
<strong>Nested Child</strong>
<p>This creates interesting depth effects with multiple layers of transparency.</p>
</BubbleContainer>
</div>
</BubbleContainer>
</div>
</div>
</section>
<section class="showcase-section">
<BubbleContainer variant="prominent" padding="lg">
<h2>Usage Notes</h2>
<div class="usage-grid">
<div class="usage-item">
<h4>Responsive Design</h4>
<p>Automatically adjusts border radius and padding on mobile devices.</p>
</div>
<div class="usage-item">
<h4>Theme Support</h4>
<p>Seamlessly switches between light and dark modes using CSS media queries.</p>
</div>
<div class="usage-item">
<h4>Performance</h4>
<p>Uses backdrop-filter for efficient blur effects with hardware acceleration.</p>
</div>
<div class="usage-item">
<h4>Accessibility</h4>
<p>Maintains proper contrast ratios in both light and dark themes.</p>
</div>
</div>
</BubbleContainer>
</section>
</div>
</div>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
<style scoped>
.about {
min-height: 100vh;
padding: 2rem 0;
}
.about-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
.page-title {
font-size: 3rem;
font-weight: 600;
text-align: center;
margin-bottom: 1rem;
background: linear-gradient(135deg, var(--color-heading), var(--color-text));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.page-subtitle {
font-size: 1.25rem;
text-align: center;
color: var(--color-text);
opacity: 0.8;
margin-bottom: 3rem;
}
.component-showcase {
display: flex;
flex-direction: column;
gap: 3rem;
}
.showcase-section {
display: flex;
flex-direction: column;
gap: 2rem;
}
.showcase-section > h2 {
font-size: 2rem;
font-weight: 500;
color: var(--color-heading);
margin-bottom: 0.5rem;
}
.section-description {
font-size: 1.1rem;
line-height: 1.6;
color: var(--color-text);
opacity: 0.9;
margin-bottom: 1rem;
}
.examples-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
align-items: start;
}
.example {
display: flex;
flex-direction: column;
gap: 1rem;
}
.example.full-width {
grid-column: 1 / -1;
}
.example > h3 {
font-size: 1.25rem;
font-weight: 500;
color: var(--color-heading);
margin-bottom: 0.5rem;
}
.example h4 {
font-size: 1.1rem;
font-weight: 500;
color: var(--color-heading);
margin-bottom: 0.75rem;
}
.example p {
line-height: 1.6;
margin-bottom: 1rem;
}
.demo-content {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.demo-tag {
background: rgba(var(--vt-c-indigo), 0.1);
color: var(--color-heading);
padding: 0.25rem 0.75rem;
border-radius: 1rem;
font-size: 0.875rem;
border: 1px solid rgba(var(--vt-c-indigo), 0.2);
}
.demo-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.demo-item {
font-size: 0.9rem;
opacity: 0.9;
}
.demo-stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-top: 1rem;
}
.stat {
text-align: center;
}
.stat-number {
font-size: 1.5rem;
font-weight: 600;
color: var(--color-heading);
}
.stat-label {
font-size: 0.875rem;
opacity: 0.8;
}
.nested-demo {
margin-top: 1rem;
}
.usage-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-top: 1rem;
}
.usage-item h4 {
font-size: 1rem;
font-weight: 500;
color: var(--color-heading);
margin-bottom: 0.5rem;
}
.usage-item p {
font-size: 0.9rem;
line-height: 1.5;
margin: 0;
opacity: 0.9;
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.about-content {
padding: 0 0.5rem;
}
.page-title {
font-size: 2.5rem;
}
.examples-grid {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.demo-stats {
grid-template-columns: 1fr;
}
.usage-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.page-title {
font-size: 2rem;
}
.page-subtitle {
font-size: 1.1rem;
}
}
</style>
</style>
Loading…
Cancel
Save