- Add comprehensive documentation in botbook/ with 12 chapters - Add botapp/ Tauri desktop application - Add botdevice/ IoT device support - Add botlib/ shared library crate - Add botmodels/ Python ML models service - Add botplugin/ browser extension - Add botserver/ reorganized server code - Add bottemplates/ bot templates - Add bottest/ integration tests - Add botui/ web UI server - Add CI/CD workflows in .forgejo/workflows/ - Add AGENTS.md and PROD.md documentation - Add dependency management scripts (DEPENDENCIES.sh/ps1) - Remove legacy src/ structure and migrations - Clean up temporary and backup files
7.9 KiB
7.9 KiB
CSS Customization
The gbtheme CSS files define the visual style of the bot UI. They are split into three layers to make them easy to extend.
Files
| File | Role |
|---|---|
main.css |
Core layout, typography, and global variables. |
components.css |
Styles for reusable UI components (buttons, cards, modals). |
responsive.css |
Media queries for mobile, tablet, and desktop breakpoints. |
CSS Variables (in main.css)
:root {
--primary-color: #2563eb;
--secondary-color: #64748b;
--background-color: #ffffff;
--text-color: #1e293b;
--border-radius: 8px;
--spacing-unit: 8px;
}
Changing a variable updates the entire theme without editing individual rules.
Extending the Theme
- Add a new variable – Append to
:rootand reference it in any selector. - Override a component – Duplicate the selector in
components.cssafter the original definition; the later rule wins. - Create a dark mode – Add a
@media (prefers-color-scheme: dark)block that redefines the variables.
@media (prefers-color-scheme: dark) {
:root {
--primary-color: #3b82f6;
--background-color: #111827;
--text-color: #f9fafb;
}
}
Best Practices
- Keep the file size small – avoid large image data URIs; store images in
assets/. - Use
remunits for font sizes; they scale with the rootfont-size. - Limit the depth of nesting; flat selectors improve performance.
All CSS files are loaded in index.html in the order: main.css, components.css, responsive.css.
Component Styling Guide
Message Bubbles
Customize chat message appearance:
/* User messages */
.message-user {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 12px 16px;
border-radius: 18px 18px 4px 18px;
max-width: 70%;
margin-left: auto;
}
/* Bot messages */
.message-bot {
background: #f7fafc;
color: #2d3748;
padding: 12px 16px;
border-radius: 18px 18px 18px 4px;
max-width: 70%;
border: 1px solid #e2e8f0;
}
/* Typing indicator */
.typing-indicator {
display: inline-flex;
padding: 16px;
background: #edf2f7;
border-radius: 18px;
}
.typing-indicator span {
height: 8px;
width: 8px;
background: #718096;
border-radius: 50%;
margin: 0 2px;
animation: typing 1.4s infinite;
}
Input Field
Style the message input area:
.input-container {
padding: 16px;
background: white;
border-top: 1px solid #e2e8f0;
}
.input-wrapper {
display: flex;
align-items: center;
background: #f7fafc;
border: 2px solid #e2e8f0;
border-radius: 24px;
padding: 8px 16px;
transition: all 0.2s;
}
.input-wrapper:focus-within {
border-color: var(--primary-color);
background: white;
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1);
}
.message-input {
flex: 1;
border: none;
background: transparent;
outline: none;
font-size: 16px;
}
.send-button {
background: var(--primary-color);
color: white;
border: none;
border-radius: 50%;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.2s;
}
.send-button:hover {
transform: scale(1.1);
}
.send-button:active {
transform: scale(0.95);
}
Buttons
Consistent button styling:
/* Primary button */
.btn-primary {
background: var(--primary-color);
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary:hover {
filter: brightness(110%);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* Secondary button */
.btn-secondary {
background: transparent;
color: var(--primary-color);
border: 2px solid var(--primary-color);
padding: 8px 18px;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.btn-secondary:hover {
background: var(--primary-color);
color: white;
}
/* Icon button */
.btn-icon {
background: transparent;
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background 0.2s;
}
.btn-icon:hover {
background: rgba(0, 0, 0, 0.05);
}
Animation Library
Entrance Animations
@keyframes slideInUp {
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scaleIn {
from {
transform: scale(0.95);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* Apply animations */
.message {
animation: slideInUp 0.3s ease-out;
}
.modal {
animation: scaleIn 0.2s ease-out;
}
Loading States
/* Spinner */
.spinner {
width: 40px;
height: 40px;
border: 3px solid #e2e8f0;
border-top-color: var(--primary-color);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Skeleton loader */
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
Responsive Design Patterns
Mobile-First Approach
/* Base mobile styles */
.container {
padding: 16px;
width: 100%;
}
/* Tablet and up */
@media (min-width: 768px) {
.container {
padding: 24px;
max-width: 768px;
margin: 0 auto;
}
}
/* Desktop */
@media (min-width: 1024px) {
.container {
padding: 32px;
max-width: 1024px;
}
}
/* Wide screens */
@media (min-width: 1440px) {
.container {
max-width: 1280px;
}
}
Touch-Friendly Styles
/* Increase touch targets on mobile */
@media (pointer: coarse) {
button, a, input, select {
min-height: 44px;
min-width: 44px;
}
.btn-primary, .btn-secondary {
padding: 12px 24px;
font-size: 16px;
}
}
/* Disable hover effects on touch devices */
@media (hover: none) {
.btn-primary:hover {
filter: none;
box-shadow: none;
}
}
Theme Variants
Dark Mode
@media (prefers-color-scheme: dark) {
:root {
--primary-color: #60a5fa;
--secondary-color: #94a3b8;
--background-color: #0f172a;
--text-color: #f1f5f9;
--border-color: #334155;
}
.message-bot {
background: #1e293b;
color: #f1f5f9;
border-color: #334155;
}
.input-wrapper {
background: #1e293b;
border-color: #334155;
}
}
High Contrast
@media (prefers-contrast: high) {
:root {
--primary-color: #0066cc;
--text-color: #000000;
--background-color: #ffffff;
}
* {
border-width: 2px !important;
}
button:focus, input:focus {
outline: 3px solid #000000 !important;
outline-offset: 2px !important;
}
}
Performance Tips
- Use CSS Variables: Change themes by updating variables, not entire stylesheets
- Minimize Specificity: Keep selectors simple for faster parsing
- Avoid Deep Nesting: Maximum 3 levels deep
- Use Transform/Opacity: For animations instead of layout properties
- Lazy Load Non-Critical CSS: Load theme variations on demand
Browser Compatibility
/* Provide fallbacks for older browsers */
.gradient-bg {
background: #3b82f6; /* Fallback */
background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
}
/* Use @supports for progressive enhancement */
@supports (backdrop-filter: blur(10px)) {
.modal-backdrop {
backdrop-filter: blur(10px);
}
}
See Also
- Theme Structure - File organization
- Chapter 4: User Interface - Applying themes to templates
- Chapter 6: BASIC - Dynamic theme switching
Next Step
Return to Chapter 5 Overview or continue to Chapter 6: BASIC Dialogs.