add slack theme

This commit is contained in:
Younes ENNAJI
2025-03-08 11:52:59 +00:00
parent 8d5cadcf66
commit 47c7cd9ad8
3 changed files with 382 additions and 0 deletions
@@ -0,0 +1,28 @@
/**
* @file PHPFlasher Slack Theme Registration
* @description Registers the Slack theme with PHPFlasher
* @author yoeunes
*/
import flasher from '../../index'
import { slackTheme } from './slack'
/**
* Register the Slack theme.
*
* This theme provides notifications styled after Slack's familiar messaging interface.
* The registration makes the theme available under the name 'slack'.
*
* The Slack theme can be used by calling:
* ```typescript
* flasher.use('theme.slack').success('Your changes have been saved');
* ```
*
* Key features:
* - Message bubbles with subtle borders and shadows
* - Colored avatar icons indicating notification type
* - Clean typography matching Slack's text style
* - Hover effects that reveal action buttons
* - Dark mode support matching Slack's dark theme
* - RTL language support
*/
flasher.addTheme('slack', slackTheme)
@@ -0,0 +1,263 @@
/**
* @file PHPFlasher Slack Theme Styles
* @description CSS styling for Slack-inspired notifications
* @author yoeunes
*/
/**
* PHPFlasher - Slack Theme
*
* Familiar notifications inspired by Slack's popular messaging platform.
* Used by millions of professionals worldwide.
*/
.fl-slack {
/* Theme variables - Define the visual appearance of Slack notifications */
/* Base colors and appearance */
--slack-bg-light: #ffffff; /* White background in light mode */
--slack-bg-dark: #1a1d21; /* Dark background in dark mode (Slack dark) */
--slack-hover-light: #f8f8f8; /* Light hover state */
--slack-hover-dark: #222529; /* Dark hover state */
--slack-text-light: #1d1c1d; /* Dark text in light mode */
--slack-text-secondary-light: #616061; /* Gray secondary text in light mode */
--slack-text-dark: #e0e0e0; /* Light text in dark mode */
--slack-text-secondary-dark: #ababad; /* Gray secondary text in dark mode */
--slack-border-light: #e0e0e0; /* Light border color */
--slack-border-dark: #393a3e; /* Dark border color */
--slack-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); /* Subtle shadow in light mode */
--slack-shadow-dark: 0 1px 0 rgba(0, 0, 0, 0.2); /* Stronger shadow in dark mode */
--slack-avatar-size: 36px; /* Size of avatar/icon container */
/* Type-specific colors matching Slack's color scheme */
--slack-success: #2bac76; /* Green */
--slack-info: #1264a3; /* Blue */
--slack-warning: #e8912d; /* Orange */
--slack-error: #e01e5a; /* Red/Pink */
/* Animation timing */
--slack-animation-duration: 150ms; /* Quick animation for responsive feel */
}
/**
* Simple fade in animation
* Quick and subtle to match Slack's responsive feel
*/
@keyframes slackFadeIn {
from {
opacity: 0; /* Start invisible */
}
to {
opacity: 1; /* End fully visible */
}
}
/**
* Base Slack theme styling
*/
.fl-slack {
position: relative;
margin: 4px 0; /* Minimal spacing between messages */
width: 100%;
max-width: 500px; /* Maximum width similar to Slack */
font-family: "Lato", "Slack-Lato", "Helvetica Neue", "Helvetica", sans-serif; /* Slack's font stack */
animation: slackFadeIn var(--slack-animation-duration) ease-out; /* Quick fade in */
/**
* Message bubble
* The main container that resembles a Slack message
*/
.fl-slack-message {
background-color: var(--slack-bg-light);
color: var(--slack-text-light);
padding: 8px 20px 8px 8px; /* More padding on right for close button */
border-radius: 4px; /* Slightly rounded corners */
display: flex;
align-items: flex-start; /* Align content to top */
transition: background-color 0.1s ease; /* Smooth hover transition */
border: 1px solid var(--slack-border-light); /* Subtle border */
box-shadow: var(--slack-shadow); /* Slight shadow for depth */
&:hover {
background-color: var(--slack-hover-light); /* Background change on hover */
}
}
/**
* Avatar container
* Colored square that holds the type icon
*/
.fl-avatar {
width: var(--slack-avatar-size); /* Fixed size avatar */
height: var(--slack-avatar-size);
border-radius: 4px; /* Slightly rounded corners */
margin-right: 8px; /* Space between avatar and text */
flex-shrink: 0; /* Prevent avatar from shrinking */
display: flex;
align-items: center;
justify-content: center;
background-color: currentColor; /* Uses the type color */
}
/**
* Type icon
* Symbol within the colored avatar
*/
.fl-type-icon {
color: white; /* White icon on colored background */
font-weight: bold; /* Bold symbol */
font-size: 16px; /* Icon size */
}
/**
* Message content container
* Holds the message text
*/
.fl-message-content {
flex: 1; /* Take available space */
min-width: 0; /* Allows text to wrap */
}
/**
* Message text styling
* Follows Slack's text appearance
*/
.fl-message-text {
font-size: 15px; /* Slack's standard message size */
line-height: 1.46668; /* Slack's line height */
word-break: break-word; /* Allows breaking long words */
}
/**
* Actions container
* Holds the close button that appears on hover
*/
.fl-actions {
visibility: hidden; /* Hidden by default */
position: absolute;
right: 6px; /* Positioned in top-right */
top: 8px;
opacity: 0; /* Transparent by default */
transition: opacity 0.1s ease; /* Smooth fade in/out */
}
/**
* Show actions on hover
* Makes the close button appear when hovering the message
*/
.fl-slack-message:hover .fl-actions {
visibility: visible; /* Show on hover */
opacity: 1; /* Fade in */
}
/**
* Close button styling
* Simple button with hover effect
*/
.fl-close {
background: none;
border: none;
color: var(--slack-text-secondary-light); /* Gray icon by default */
cursor: pointer;
padding: 4px;
border-radius: 4px; /* Slightly rounded corners */
display: flex;
align-items: center;
justify-content: center;
&:hover {
color: var(--slack-text-light); /* Darker on hover */
background-color: var(--slack-hover-light); /* Light background on hover */
}
}
/**
* Type-specific styling
* Each notification type gets its own colored avatar
*/
&.fl-success {
.fl-avatar {
color: var(--slack-success); /* Green avatar */
}
}
&.fl-info {
.fl-avatar {
color: var(--slack-info); /* Blue avatar */
}
}
&.fl-warning {
.fl-avatar {
color: var(--slack-warning); /* Orange avatar */
}
}
&.fl-error {
.fl-avatar {
color: var(--slack-error); /* Red avatar */
}
}
/**
* RTL Support
* Right-to-left language direction support
*/
&.fl-rtl {
direction: rtl; /* Right-to-left text */
.fl-avatar {
margin-right: 0;
margin-left: 8px; /* Swap margins */
}
.fl-username {
margin-right: 0;
margin-left: 4px; /* Swap margins */
}
.fl-actions {
right: auto;
left: 6px; /* Move to top-left */
}
.fl-slack-message {
padding: 8px 8px 8px 20px; /* Swap padding */
}
}
/**
* Accessibility
* Respects reduced motion preferences
*/
@media (prefers-reduced-motion: reduce) {
animation: none; /* Disable animation */
}
}
/**
* Dark mode support
* Slack-like dark theme
*/
body.fl-dark .fl-slack,
html.fl-dark .fl-slack,
.fl-slack.fl-auto-dark {
.fl-slack-message {
background-color: var(--slack-bg-dark); /* Dark background */
color: var(--slack-text-dark); /* Light text */
border-color: var(--slack-border-dark); /* Darker border */
box-shadow: var(--slack-shadow-dark); /* Stronger shadow */
&:hover {
background-color: var(--slack-hover-dark); /* Dark hover state */
}
}
.fl-close {
color: var(--slack-text-secondary-dark); /* Lighter gray icon */
&:hover {
color: var(--slack-text-dark); /* White on hover */
background-color: var(--slack-hover-dark); /* Dark background on hover */
}
}
}
@@ -0,0 +1,91 @@
/**
* @file PHPFlasher Slack Theme Implementation
* @description Notifications styled after Slack's messaging interface
* @author yoeunes
*/
import './slack.scss'
import type { Envelope } from '../../types'
/**
* Slack notification theme for PHPFlasher.
*
* The Slack theme replicates the familiar messaging interface from Slack with:
* - Message bubbles with subtle borders and shadows
* - Avatar-like colored icons indicating notification type
* - Clean typography matching Slack's text style
* - Hover effects that reveal action buttons
* - Simple, fast animations for a responsive feel
*
* This theme creates a familiar experience for users accustomed to Slack's
* widely-used interface, providing a sense of comfort and familiarity.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { slackTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('slack', slackTheme);
*
* // Use the theme
* flasher.use('theme.slack').success('Your changes have been saved');
* ```
*/
export const slackTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a Slack-styled message bubble with colored avatar icon,
* message text, and a close button that appears on hover.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => {
const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite'
/**
* Gets the appropriate type icon based on notification type.
* Each type has a simple symbol within a colored background.
*
* @returns HTML markup for the type icon
*/
const getTypeIcon = () => {
switch (type) {
case 'success':
return `<div class="fl-type-icon fl-success-icon">✓</div>`
case 'error':
return `<div class="fl-type-icon fl-error-icon">✕</div>`
case 'warning':
return `<div class="fl-type-icon fl-warning-icon">!</div>`
case 'info':
return `<div class="fl-type-icon fl-info-icon">i</div>`
}
return ''
}
return `
<div class="fl-slack fl-${type}" role="${role}" aria-live="${ariaLive}" aria-atomic="true">
<div class="fl-slack-message">
<div class="fl-avatar">
${getTypeIcon()}
</div>
<div class="fl-message-content">
<div class="fl-message-text">${message}</div>
</div>
<div class="fl-actions">
<button class="fl-close" aria-label="Close ${type} message">
<svg viewBox="0 0 20 20" width="16" height="16">
<path fill="currentColor" d="M10 8.586L6.707 5.293a1 1 0 00-1.414 1.414L8.586 10l-3.293 3.293a1 1 0 101.414 1.414L10 11.414l3.293 3.293a1 1 0 001.414-1.414L11.414 10l3.293-3.293a1 1 0 00-1.414-1.414L10 8.586z"/>
</svg>
</button>
</div>
</div>
</div>`
},
}