Simplify JSDoc and SCSS comments in theme files and entry points

Remove verbose JSDoc and SCSS comments from:
- Theme TypeScript files (17 amazon, amber, aurora, crystal, emerald, facebook, flasher, google, ios, jade, material, minimal, neon, onyx, ruby, sapphire, slack)
- Theme SCSS files (21 theme stylesheets including icons, progress, wrapper, and all theme variants)
- Theme index.ts registration files (17 files)
- Entry point and export files (index.ts, exports.ts)
- Adapter index.ts files (Noty, Notyf, SweetAlert, Toastr)
- Remaining plugin files (toastr.ts, notyf.ts)

Kept type annotations and removed only descriptive comments, examples,
and file headers following Symfony's concise documentation style.
This commit is contained in:
Younes ENNAJI
2026-01-16 00:40:14 +01:00
parent cc9fa57c4a
commit 61f991f379
65 changed files with 47 additions and 2401 deletions
-31
View File
@@ -1,38 +1,7 @@
/**
* @file Noty Plugin Entry Point
* @description Registers the Noty plugin with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '@flasher/flasher' import flasher from '@flasher/flasher'
import NotyPlugin from './noty' import NotyPlugin from './noty'
/**
* Create and register the Noty plugin with PHPFlasher.
*
* This enables using Noty for displaying notifications through
* the PHPFlasher API.
*
* @example
* ```typescript
* // With the plugin already registered
* import flasher from '@flasher/flasher';
*
* flasher.use('noty').success('Operation completed');
* ```
*/
const noty = new NotyPlugin() const noty = new NotyPlugin()
flasher.addPlugin('noty', noty) flasher.addPlugin('noty', noty)
/**
* Export the Noty plugin instance.
*
* This allows direct access to the plugin if needed.
*
* @example
* ```typescript
* import noty from '@flasher/flasher-noty';
*
* noty.success('Operation completed');
* ```
*/
export default noty export default noty
-38
View File
@@ -1,47 +1,9 @@
/**
* @file Notyf Plugin Entry Point
* @description Registers the Notyf plugin with PHPFlasher
* @author Younes ENNAJI
*/
import './notyf.scss' import './notyf.scss'
import flasher from '@flasher/flasher' import flasher from '@flasher/flasher'
import NotyfPlugin from './notyf' import NotyfPlugin from './notyf'
/**
* Create and register the Notyf plugin with PHPFlasher.
*
* This enables using Notyf for displaying notifications through
* the PHPFlasher API.
*
* @example
* ```typescript
* // With the plugin already registered
* import flasher from '@flasher/flasher';
*
* flasher.use('notyf').success('Operation completed');
*
* // With custom options
* flasher.use('notyf').info('This notification has custom options', null, {
* duration: 3000,
* ripple: true,
* dismissible: true
* });
* ```
*/
const notyf = new NotyfPlugin() const notyf = new NotyfPlugin()
flasher.addPlugin('notyf', notyf) flasher.addPlugin('notyf', notyf)
/**
* Export the Notyf plugin instance.
*
* This allows direct access to the plugin if needed.
*
* @example
* ```typescript
* import notyf from '@flasher/flasher-notyf';
*
* notyf.success('Operation completed');
* ```
*/
export default notyf export default notyf
@@ -1,15 +1,4 @@
/**
* Notyf custom styles for PHPFlasher
*
* These styles define custom icons for additional notification types
* (info and warning) beyond the default success and error types
* provided by Notyf.
*
* @author Younes ENNAJI
*/
.notyf { .notyf {
// Common styles for custom icons
&__icon { &__icon {
&--warning, &--warning,
&--info { &--info {
@@ -32,7 +21,6 @@
} }
} }
// Info icon (i)
&--info { &--info {
&::before, &::before,
&::after { &::after {
@@ -43,13 +31,11 @@
transform: translateX(-50%); transform: translateX(-50%);
} }
// Vertical line
&::before { &::before {
top: 0.4em; top: 0.4em;
height: 0.38em; height: 0.38em;
} }
// Dot and shadow
&::after { &::after {
top: 0.21em; top: 0.21em;
height: 0.13em; height: 0.13em;
@@ -57,7 +43,6 @@
} }
} }
// Warning icon (!)
&--warning { &--warning {
&::before, &::before,
&::after { &::after {
@@ -68,13 +53,11 @@
transform: translateX(-50%); transform: translateX(-50%);
} }
// Exclamation vertical line
&::before { &::before {
top: 0.21em; top: 0.21em;
height: 0.38em; height: 0.38em;
} }
// Exclamation dot
&::after { &::after {
top: 0.65em; top: 0.65em;
height: 0.13em; height: 0.13em;
+4 -75
View File
@@ -1,8 +1,3 @@
/**
* @file Notyf Plugin Implementation
* @description PHPFlasher integration with the Notyf notification library
* @author Younes ENNAJI
*/
import { AbstractPlugin } from '@flasher/flasher/dist/plugin' import { AbstractPlugin } from '@flasher/flasher/dist/plugin'
import type { Envelope, Options } from '@flasher/flasher/dist/types' import type { Envelope, Options } from '@flasher/flasher/dist/types'
@@ -10,41 +5,9 @@ import { Notyf } from 'notyf'
import type { INotyfOptions } from 'notyf/notyf.options' import type { INotyfOptions } from 'notyf/notyf.options'
import 'notyf/notyf.min.css' import 'notyf/notyf.min.css'
/**
* Plugin implementation for Notyf notification library.
*
* The NotyfPlugin integrates the lightweight Notyf library with PHPFlasher,
* allowing for simple, responsive, and customizable toast notifications.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import NotyfPlugin from '@flasher/flasher-notyf';
*
* // Register the plugin
* flasher.addPlugin('notyf', new NotyfPlugin());
*
* // Show a notification
* flasher.use('notyf').success('Operation completed');
* ```
*/
export default class NotyfPlugin extends AbstractPlugin { export default class NotyfPlugin extends AbstractPlugin {
/**
* The Notyf instance used to display notifications.
* Lazy-initialized when first needed.
*
* @private
*/
private notyf?: Notyf private notyf?: Notyf
/**
* Creates Notyf notifications from envelopes.
*
* This method transforms PHPFlasher envelopes into Notyf notifications
* and displays them using the Notyf library.
*
* @param envelopes - Array of notification envelopes to render
*/
public renderEnvelopes(envelopes: Envelope[]): void { public renderEnvelopes(envelopes: Envelope[]): void {
if (!this.notyf) { if (!this.notyf) {
this.initializeNotyf() this.initializeNotyf()
@@ -52,7 +15,6 @@ export default class NotyfPlugin extends AbstractPlugin {
envelopes.forEach((envelope) => { envelopes.forEach((envelope) => {
try { try {
// Merge envelope properties with its options for Notyf compatibility
const options = { ...envelope, ...envelope.options } const options = { ...envelope, ...envelope.options }
this.notyf?.open(options) this.notyf?.open(options)
} catch (error) { } catch (error) {
@@ -60,13 +22,9 @@ export default class NotyfPlugin extends AbstractPlugin {
} }
}) })
// Ensure compatibility with Turbo/Hotwire by marking containers
// as temporary elements that should be preserved during page transitions
try { try {
if (this.notyf) { if (this.notyf) {
// @ts-expect-error - Accessing internal Notyf properties
const container = this.notyf.view.container const container = this.notyf.view.container
// @ts-expect-error - Accessing internal Notyf properties
const a11yContainer = this.notyf.view.a11yContainer const a11yContainer = this.notyf.view.a11yContainer
if (container && container.dataset) { if (container && container.dataset) {
@@ -82,81 +40,52 @@ export default class NotyfPlugin extends AbstractPlugin {
} }
} }
/**
* Apply global options to Notyf.
*
* This method configures the Notyf library with the provided options,
* which will affect all subsequently created notifications. It also
* adds support for additional notification types beyond the default
* success and error types.
*
* @param options - Configuration options for Notyf
*/
public renderOptions(options: Options): void { public renderOptions(options: Options): void {
if (!options) { if (!options) {
return return
} }
const notyfOptions = { const notyfOptions = {
duration: options.duration || 10000, // Default timeout of 10 seconds duration: options.duration || 10000,
...options, ...options,
} as unknown as INotyfOptions } as unknown as INotyfOptions
// Initialize types array if not present
notyfOptions.types = notyfOptions.types || [] notyfOptions.types = notyfOptions.types || []
// Add support for info notifications with custom icon
this.addTypeIfNotExists(notyfOptions.types, { this.addTypeIfNotExists(notyfOptions.types, {
type: 'info', type: 'info',
className: 'notyf__toast--info', className: 'notyf__toast--info',
background: '#5784E5', // Blue color background: '#5784E5',
icon: { icon: {
className: 'notyf__icon--info', className: 'notyf__icon--info',
tagName: 'i', tagName: 'i',
}, },
}) })
// Add support for warning notifications with custom icon
this.addTypeIfNotExists(notyfOptions.types, { this.addTypeIfNotExists(notyfOptions.types, {
type: 'warning', type: 'warning',
className: 'notyf__toast--warning', className: 'notyf__toast--warning',
background: '#E3A008', // Amber color background: '#E3A008',
icon: { icon: {
className: 'notyf__icon--warning', className: 'notyf__icon--warning',
tagName: 'i', tagName: 'i',
}, },
}) })
// Create or update Notyf instance with new options
this.notyf = this.notyf || new Notyf(notyfOptions) this.notyf = this.notyf || new Notyf(notyfOptions)
} }
/**
* Initialize the Notyf instance with default options if not already created.
*
* @private
*/
private initializeNotyf(): void { private initializeNotyf(): void {
if (!this.notyf) { if (!this.notyf) {
// Default configuration with info and warning types
this.renderOptions({ this.renderOptions({
duration: 10000, // 10 seconds duration: 10000,
position: { x: 'right', y: 'top' }, position: { x: 'right', y: 'top' },
dismissible: true, dismissible: true,
}) })
} }
} }
/**
* Add a notification type to Notyf if it doesn't already exist.
* Prevents duplicate type definitions.
*
* @param types - Array of notification types
* @param newType - New type to add
* @private
*/
private addTypeIfNotExists(types: any[], newType: any): void { private addTypeIfNotExists(types: any[], newType: any): void {
// Check if type already exists
const exists = types.some((type) => type.type === newType.type) const exists = types.some((type) => type.type === newType.type)
if (!exists) { if (!exists) {
types.push(newType) types.push(newType)
-52
View File
@@ -1,56 +1,4 @@
/**
* @file PHPFlasher Type Exports
* @description Re-exports types and interfaces for TypeScript users
* @author Younes ENNAJI
*/
/**
* Re-export all types and interfaces.
*
* This allows TypeScript users to import specific types:
*
* @example
* ```typescript
* import { Options, Envelope } from '@flasher/flasher/exports';
* ```
*/
export * from './types' export * from './types'
/**
* Re-export the AbstractPlugin class.
*
* This is useful for creating custom plugins.
*
* @example
* ```typescript
* import { AbstractPlugin } from '@flasher/flasher/exports';
*
* class MyPlugin extends AbstractPlugin {
* // Implementation
* }
* ```
*/
export { AbstractPlugin } from './plugin' export { AbstractPlugin } from './plugin'
/**
* Re-export the FlasherPlugin class.
*
* This allows creating custom theme-based plugins.
*
* @example
* ```typescript
* import { FlasherPlugin } from '@flasher/flasher/exports';
* import myTheme from './my-theme';
*
* const plugin = new FlasherPlugin(myTheme);
* ```
*/
export { default as FlasherPlugin } from './flasher-plugin' export { default as FlasherPlugin } from './flasher-plugin'
/**
* Re-export the default flasher instance.
*
* This ensures consistency whether importing from the main package
* or from the exports file.
*/
export { default } from './index' export { default } from './index'
-33
View File
@@ -1,44 +1,11 @@
/**
* @file PHPFlasher Main Entry Point
* @description Creates and exports the default PHPFlasher instance
* @author Younes ENNAJI
*/
import Flasher from './flasher' import Flasher from './flasher'
import { flasherTheme } from './themes' import { flasherTheme } from './themes'
/**
* Create and configure the default Flasher instance.
*
* This singleton instance is the main entry point for the PHPFlasher library.
* It comes pre-configured with the default theme.
*/
const flasher = new Flasher() const flasher = new Flasher()
flasher.addTheme('flasher', flasherTheme) flasher.addTheme('flasher', flasherTheme)
/**
* Make the flasher instance available globally for browser scripts.
*
* This allows PHPFlasher to be used in vanilla JavaScript without
* module imports.
*
* @example
* ```javascript
* // In a browser script
* window.flasher.success('Operation completed');
* ```
*/
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
window.flasher = flasher window.flasher = flasher
} }
/**
* Default export of the pre-configured flasher instance.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
*
* flasher.success('Operation completed');
* ```
*/
export default flasher export default flasher
@@ -1,69 +1,44 @@
/**
* @file PHPFlasher Amazon Theme Styles
* @description CSS styling for Amazon-inspired notifications
* @author Younes ENNAJI
*/
/**
* Amazon Theme
*
* A notification style inspired by Amazon's e-commerce platform.
* Features distinct color scheme and design language that mimics
* Amazon's alert components.
*/
.fl-amazon { .fl-amazon {
/* Theme-specific variables --amazon-bg-light: #ffffff;
* These define the color palette and styling attributes unique to the Amazon theme --amazon-bg-dark: #202124;
*/ --amazon-text-light: #0f1111;
--amazon-text-secondary-light: #565959;
--amazon-text-dark: #ffffff;
--amazon-text-secondary-dark: #b6b6b6;
--amazon-border-light: #ddd;
--amazon-border-dark: #3f3f3f;
--amazon-yellow: #ffd814;
--amazon-orange: #ff9900;
/* Base colors */ --amazon-success-bg: #f0fff5;
--amazon-bg-light: #ffffff; /* Light mode background */ --amazon-success-border: #7fda95;
--amazon-bg-dark: #202124; /* Dark mode background */ --amazon-success-icon: #007600;
--amazon-text-light: #0f1111; /* Light mode primary text */ --amazon-info-bg: #f3f9ff;
--amazon-text-secondary-light: #565959; /* Light mode secondary text */ --amazon-info-border: #7fb4da;
--amazon-text-dark: #ffffff; /* Dark mode primary text */ --amazon-info-icon: #0066c0;
--amazon-text-secondary-dark: #b6b6b6; /* Dark mode secondary text */ --amazon-warning-bg: #fffcf3;
--amazon-border-light: #ddd; /* Light mode border */ --amazon-warning-border: #ffd996;
--amazon-border-dark: #3f3f3f; /* Dark mode border */ --amazon-warning-icon: #c45500;
--amazon-yellow: #ffd814; /* Amazon's signature yellow */ --amazon-error-bg: #fff5f5;
--amazon-orange: #ff9900; /* Amazon's signature orange */ --amazon-error-border: #ff8f8f;
--amazon-error-icon: #c40000;
/* Type-specific colors - Light mode */ --amazon-success-bg-dark: #082d11;
--amazon-success-bg: #f0fff5; /* Success background */ --amazon-success-border-dark: #1e6e2c;
--amazon-success-border: #7fda95; /* Success border */ --amazon-success-icon-dark: #7fda95;
--amazon-success-icon: #007600; /* Success icon color */ --amazon-info-bg-dark: #0a1c2e;
--amazon-info-bg: #f3f9ff; /* Info background */ --amazon-info-border-dark: #1a5183;
--amazon-info-border: #7fb4da; /* Info border */ --amazon-info-icon-dark: #7fb4da;
--amazon-info-icon: #0066c0; /* Info icon color */ --amazon-warning-bg-dark: #342a0a;
--amazon-warning-bg: #fffcf3; /* Warning background */ --amazon-warning-border-dark: #705711;
--amazon-warning-border: #ffd996; /* Warning border */ --amazon-warning-icon-dark: #ffd996;
--amazon-warning-icon: #c45500; /* Warning icon color */ --amazon-error-bg-dark: #2b0c0c;
--amazon-error-bg: #fff5f5; /* Error background */ --amazon-error-border-dark: #721c1c;
--amazon-error-border: #ff8f8f; /* Error border */ --amazon-error-icon-dark: #ff8f8f;
--amazon-error-icon: #c40000; /* Error icon color */
/* Type-specific colors - Dark mode */ --amazon-animation-duration: 0.2s;
--amazon-success-bg-dark: #082d11; /* Dark mode success background */
--amazon-success-border-dark: #1e6e2c; /* Dark mode success border */
--amazon-success-icon-dark: #7fda95; /* Dark mode success icon */
--amazon-info-bg-dark: #0a1c2e; /* Dark mode info background */
--amazon-info-border-dark: #1a5183; /* Dark mode info border */
--amazon-info-icon-dark: #7fb4da; /* Dark mode info icon */
--amazon-warning-bg-dark: #342a0a; /* Dark mode warning background */
--amazon-warning-border-dark: #705711; /* Dark mode warning border */
--amazon-warning-icon-dark: #ffd996; /* Dark mode warning icon */
--amazon-error-bg-dark: #2b0c0c; /* Dark mode error background */
--amazon-error-border-dark: #721c1c; /* Dark mode error border */
--amazon-error-icon-dark: #ff8f8f; /* Dark mode error icon */
/* Animation timing */
--amazon-animation-duration: 0.2s; /* Duration for all animations */
} }
/**
* Entrance animation for Amazon theme
* Slides in from the top with a fade effect
*/
@keyframes amazonSlideDown { @keyframes amazonSlideDown {
from { from {
opacity: 0; opacity: 0;
@@ -75,19 +50,12 @@
} }
} }
/**
* Base Amazon theme styling
*/
.fl-amazon { .fl-amazon {
position: relative; position: relative;
margin: 10px 0; margin: 10px 0;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
animation: amazonSlideDown var(--amazon-animation-duration) ease-out; animation: amazonSlideDown var(--amazon-animation-duration) ease-out;
/**
* Main alert container
* Contains all notification elements
*/
.fl-amazon-alert { .fl-amazon-alert {
background-color: var(--amazon-bg-light); background-color: var(--amazon-bg-light);
color: var(--amazon-text-light); color: var(--amazon-text-light);
@@ -99,64 +67,39 @@
border-left-width: 4px; border-left-width: 4px;
} }
/**
* Content area
* Contains icon and text elements
*/
.fl-alert-content { .fl-alert-content {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
flex: 1; flex: 1;
} }
/**
* Icon container
* Holds the SVG icon
*/
.fl-icon-container { .fl-icon-container {
margin-right: 12px; margin-right: 12px;
flex-shrink: 0; flex-shrink: 0;
color: currentColor; color: currentColor;
} }
/**
* Text content container
* Holds title and message
*/
.fl-text-content { .fl-text-content {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
} }
/**
* Alert title styling
*/
.fl-alert-title { .fl-alert-title {
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 16px;
margin-bottom: 4px; margin-bottom: 4px;
} }
/**
* Alert message styling
*/
.fl-alert-message { .fl-alert-message {
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
color: var(--amazon-text-secondary-light); color: var(--amazon-text-secondary-light);
} }
/**
* Action buttons container
* Currently holds only the close button
*/
.fl-alert-actions { .fl-alert-actions {
margin-left: 16px; margin-left: 16px;
} }
/**
* Close button styling
*/
.fl-close { .fl-close {
background: none; background: none;
border: none; border: none;
@@ -177,10 +120,6 @@
} }
} }
/**
* Type-specific styling
* Each notification type has its own color scheme
*/
&.fl-success { &.fl-success {
.fl-amazon-alert { .fl-amazon-alert {
background-color: var(--amazon-success-bg); background-color: var(--amazon-success-bg);
@@ -225,10 +164,6 @@
} }
} }
/**
* Right-to-Left language support
* Adjusts layout for RTL text direction
*/
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
@@ -248,19 +183,11 @@
} }
} }
/**
* Accessibility support
* Respects reduced motion preferences
*/
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
animation: none; animation: none;
} }
} }
/**
* Dark mode support
* Different styling applied in dark mode
*/
body.fl-dark .fl-amazon, body.fl-dark .fl-amazon,
html.fl-dark .fl-amazon, html.fl-dark .fl-amazon,
.fl-amazon.fl-auto-dark { .fl-amazon.fl-auto-dark {
@@ -284,9 +211,6 @@ html.fl-dark .fl-amazon,
} }
} }
/**
* Type-specific dark mode styling
*/
&.fl-success { &.fl-success {
.fl-amazon-alert { .fl-amazon-alert {
background-color: var(--amazon-success-bg-dark); background-color: var(--amazon-success-bg-dark);
@@ -1,56 +1,14 @@
/**
* @file PHPFlasher Amazon Theme Implementation
* @description Notification style inspired by Amazon's e-commerce platform
* @author Younes ENNAJI
*/
import './amazon.scss' import './amazon.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Amazon-inspired notification theme for PHPFlasher.
*
* This theme mimics the design language of Amazon's alert and notification
* components with:
* - Type-specific icons and colors
* - Clear visual hierarchy
* - Accessible structure
* - Responsive layout
* - Dark mode support
* - RTL language support
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { amazonTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('amazon', amazonTheme);
*
* // Use the theme
* flasher.use('theme.amazon').success('Your order has been placed');
* ```
*/
export const amazonTheme = { export const amazonTheme = {
/**
* Renders a notification envelope as HTML.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
/**
* Gets the SVG icon based on notification type.
* Each notification type has a unique icon for visual distinction.
*
* @returns SVG markup for the icon
*/
const getAlertIcon = () => { const getAlertIcon = () => {
switch (type) { switch (type) {
case 'success': case 'success':
@@ -73,12 +31,6 @@ export const amazonTheme = {
return '' return ''
} }
/**
* Gets the title text based on notification type.
* Provides consistent and recognizable heading text for each type.
*
* @returns Title string for the notification
*/
const getAlertTitle = () => { const getAlertTitle = () => {
switch (type) { switch (type) {
case 'success': return 'Success!' case 'success': return 'Success!'
@@ -1,27 +1,4 @@
/**
* @file PHPFlasher Amazon Theme Registration
* @description Registers the Amazon theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { amazonTheme } from './amazon' import { amazonTheme } from './amazon'
/**
* Register the Amazon theme.
*
* This theme provides notifications styled after Amazon's design system.
* The registration makes the theme available under the name 'amazon'.
*
* The Amazon theme can be used by calling:
* ```typescript
* flasher.use('theme.amazon').success('Your order has been placed');
* ```
*
* Key features:
* - Clean, minimal design inspired by Amazon's alert components
* - Type-specific colors and icons
* - Accessible structure with appropriate ARIA roles and attributes
* - Support for both light and dark modes
* - RTL language support
*/
flasher.addTheme('amazon', amazonTheme) flasher.addTheme('amazon', amazonTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Amber Theme Styles
* @description CSS styling for the modern, elegant Amber theme
* @author Younes ENNAJI
*/ */
/**
* Amber Theme Variables * Amber Theme Variables
* *
* Custom properties that define the appearance of Amber theme notifications. * Custom properties that define the appearance of Amber theme notifications.
@@ -18,15 +12,12 @@
--amber-shadow: 0 5px 15px rgba(0, 0, 0, 0.08); /* Light mode shadow */ --amber-shadow: 0 5px 15px rgba(0, 0, 0, 0.08); /* Light mode shadow */
--amber-shadow-dark: 0 5px 15px rgba(0, 0, 0, 0.2); /* Dark mode shadow */ --amber-shadow-dark: 0 5px 15px rgba(0, 0, 0, 0.2); /* Dark mode shadow */
--amber-border-radius: 4px; /* Border radius for notifications */ --amber-border-radius: 4px; /* Border radius for notifications */
/* Type-specific colors */ /* Type-specific colors */
--amber-success: #10b981; /* Success color (green) */ --amber-success: #10b981; /* Success color (green) */
--amber-info: #3b82f6; /* Info color (blue) */ --amber-info: #3b82f6; /* Info color (blue) */
--amber-warning: #f59e0b; /* Warning color (orange) */ --amber-warning: #f59e0b; /* Warning color (orange) */
--amber-error: #ef4444; /* Error color (red) */ --amber-error: #ef4444; /* Error color (red) */
} }
/**
* Entrance animation for Amber theme * Entrance animation for Amber theme
* Slides in from the top with a subtle fade effect * Slides in from the top with a subtle fade effect
*/ */
@@ -40,8 +31,6 @@
transform: translateY(0); transform: translateY(0);
} }
} }
/**
* Base Amber theme styling * Base Amber theme styling
* Defines the core appearance and behavior of notifications * Defines the core appearance and behavior of notifications
*/ */
@@ -56,7 +45,6 @@
animation: amberIn 0.3s ease-out; animation: amberIn 0.3s ease-out;
font-family: var(--fl-font), serif; font-family: var(--fl-font), serif;
will-change: transform, opacity; will-change: transform, opacity;
/** /**
* Content container * Content container
* Holds all notification elements in a flexbox layout * Holds all notification elements in a flexbox layout
@@ -65,7 +53,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
} }
/** /**
* Icon styling * Icon styling
* The icon is rendered by the core icons.scss * The icon is rendered by the core icons.scss
@@ -74,7 +61,6 @@
font-size: 1.85em; font-size: 1.85em;
margin-right: 0.8rem; margin-right: 0.8rem;
} }
/** /**
* Text container * Text container
* Takes up available space with flex: 1 * Takes up available space with flex: 1
@@ -82,7 +68,6 @@
.fl-text { .fl-text {
flex: 1; flex: 1;
} }
/** /**
* Message styling * Message styling
* Main notification text with optimized readability * Main notification text with optimized readability
@@ -91,7 +76,6 @@
font-size: 0.875em; font-size: 0.875em;
line-height: 1.4; line-height: 1.4;
} }
/** /**
* Close button styling * Close button styling
* Minimal button with hover effect * Minimal button with hover effect
@@ -108,12 +92,10 @@
flex-shrink: 0; flex-shrink: 0;
color: currentColor; color: currentColor;
touch-action: manipulation; touch-action: manipulation;
&:hover, &:focus { &:hover, &:focus {
opacity: 1; opacity: 1;
} }
} }
/** /**
* Progress bar container * Progress bar container
* Positioned at the bottom of the notification * Positioned at the bottom of the notification
@@ -125,7 +107,6 @@
bottom: 0; bottom: 0;
height: 3px; height: 3px;
overflow: hidden; overflow: hidden;
/** /**
* Progress indicator * Progress indicator
* Animated by JavaScript to show remaining time * Animated by JavaScript to show remaining time
@@ -135,7 +116,6 @@
width: 100%; width: 100%;
} }
} }
/** /**
* Type-specific styling for close button * Type-specific styling for close button
* Each notification type gets its own color for the close button * Each notification type gets its own color for the close button
@@ -145,47 +125,39 @@
color: var(--amber-success); color: var(--amber-success);
} }
} }
&.fl-info { &.fl-info {
.fl-close { .fl-close {
color: var(--amber-info); color: var(--amber-info);
} }
} }
&.fl-warning { &.fl-warning {
.fl-close { .fl-close {
color: var(--amber-warning); color: var(--amber-warning);
} }
} }
&.fl-error { &.fl-error {
.fl-close { .fl-close {
color: var(--amber-error); color: var(--amber-error);
} }
} }
/** /**
* RTL (Right-to-Left) language support * RTL (Right-to-Left) language support
* Adjusts layout for RTL text direction * Adjusts layout for RTL text direction
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-icon { .fl-icon {
margin-right: 0; margin-right: 0;
margin-left: 0.8rem; margin-left: 0.8rem;
} }
.fl-close { .fl-close {
margin-left: 0; margin-left: 0;
margin-right: 1rem; margin-right: 1rem;
} }
.fl-progress .fl-progress { .fl-progress .fl-progress {
transform-origin: right center; transform-origin: right center;
} }
} }
/** /**
* Accessibility support * Accessibility support
* Respects reduced motion preferences * Respects reduced motion preferences
@@ -194,8 +166,6 @@
animation: none; animation: none;
} }
} }
/**
* Dark mode support * Dark mode support
* Applies dark theme colors when in dark mode * Applies dark theme colors when in dark mode
*/ */
@@ -1,52 +1,10 @@
/**
* @file PHPFlasher Amber Theme Implementation
* @description Modern, elegant notification theme with refined aesthetics
* @author Younes ENNAJI
*/
import './amber.scss' import './amber.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Amber notification theme for PHPFlasher.
*
* The Amber theme provides a clean, minimal design that focuses on
* content readability while maintaining visual appeal. It features:
* - Minimalist design with clean lines
* - Subtle animations and transitions
* - Built-in progress indicator
* - Accessible structure
* - Dark mode support
* - RTL language support
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { amberTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('amber', amberTheme);
*
* // Use the theme
* flasher.use('theme.amber').success('Your changes have been saved');
* ```
*/
export const amberTheme = { export const amberTheme = {
/**
* Renders a notification envelope as HTML.
*
* Generates a clean, accessible notification element with:
* - Type-specific icon
* - Message content
* - Close button
* - Progress indicator
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,28 +1,4 @@
/**
* @file PHPFlasher Amber Theme Registration
* @description Registers the Amber theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { amberTheme } from './amber' import { amberTheme } from './amber'
/**
* Register the Amber theme.
*
* This theme provides a modern, clean notification style with elegant aesthetics.
* The registration makes the theme available under the name 'amber'.
*
* The Amber theme can be used by calling:
* ```typescript
* flasher.use('theme.amber').success('Your changes have been saved');
* ```
*
* Key features:
* - Modern, minimalist design
* - Clean typography with optimal readability
* - Subtle entrance animation
* - Progress indicator
* - Full dark mode support
* - RTL language support
*/
flasher.addTheme('amber', amberTheme) flasher.addTheme('amber', amberTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Aurora Theme Styles
* @description CSS styling for the elegant glass-morphism Aurora theme
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Aurora Theme * PHPFlasher - Aurora Theme
* *
* Elegant notifications with subtle gradients, backdrop blur, and soft edges. * Elegant notifications with subtle gradients, backdrop blur, and soft edges.
@@ -12,7 +6,6 @@
*/ */
.fl-aurora { .fl-aurora {
/* Theme variables - Defines the visual appearance of Aurora notifications */ /* Theme variables - Defines the visual appearance of Aurora notifications */
/* Base colors and appearance */ /* Base colors and appearance */
--aurora-bg-light: rgba(255, 255, 255); /* Semi-transparent light background */ --aurora-bg-light: rgba(255, 255, 255); /* Semi-transparent light background */
--aurora-bg-dark: rgba(20, 20, 28); /* Semi-transparent dark background */ --aurora-bg-dark: rgba(20, 20, 28); /* Semi-transparent dark background */
@@ -22,21 +15,17 @@
--aurora-shadow-dark: 0 10px 30px rgba(0, 0, 0, 0.16); /* Dark mode shadow */ --aurora-shadow-dark: 0 10px 30px rgba(0, 0, 0, 0.16); /* Dark mode shadow */
--aurora-border-radius: 4px; /* Rounded corners radius */ --aurora-border-radius: 4px; /* Rounded corners radius */
--aurora-blur: 15px; /* Amount of backdrop blur */ --aurora-blur: 15px; /* Amount of backdrop blur */
/* Gradient overlays for each notification type - Light mode */ /* Gradient overlays for each notification type - Light mode */
--aurora-success-gradient: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, rgba(16, 185, 129, 0.2) 100%); --aurora-success-gradient: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, rgba(16, 185, 129, 0.2) 100%);
--aurora-info-gradient: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(59, 130, 246, 0.2) 100%); --aurora-info-gradient: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(59, 130, 246, 0.2) 100%);
--aurora-warning-gradient: linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, rgba(245, 158, 11, 0.2) 100%); --aurora-warning-gradient: linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, rgba(245, 158, 11, 0.2) 100%);
--aurora-error-gradient: linear-gradient(135deg, rgba(239, 68, 68, 0.08) 0%, rgba(239, 68, 68, 0.2) 100%); --aurora-error-gradient: linear-gradient(135deg, rgba(239, 68, 68, 0.08) 0%, rgba(239, 68, 68, 0.2) 100%);
/* Type-specific accent colors for progress bars */ /* Type-specific accent colors for progress bars */
--aurora-success: #10b981; /* Success color (green) */ --aurora-success: #10b981; /* Success color (green) */
--aurora-info: #3b82f6; /* Info color (blue) */ --aurora-info: #3b82f6; /* Info color (blue) */
--aurora-warning: #f59e0b; /* Warning color (orange) */ --aurora-warning: #f59e0b; /* Warning color (orange) */
--aurora-error: #ef4444; /* Error color (red) */ --aurora-error: #ef4444; /* Error color (red) */
} }
/**
* Entrance animation for Aurora theme * Entrance animation for Aurora theme
* Combines fade-in with a subtle scale effect * Combines fade-in with a subtle scale effect
*/ */
@@ -50,8 +39,6 @@
transform: translateY(0) scale(1); /* End at full size */ transform: translateY(0) scale(1); /* End at full size */
} }
} }
/**
* Base Aurora theme styling * Base Aurora theme styling
*/ */
.fl-aurora { .fl-aurora {
@@ -67,11 +54,9 @@
font-family: var(--fl-font), sans-serif; font-family: var(--fl-font), sans-serif;
overflow: hidden; /* Prevents gradient from leaking */ overflow: hidden; /* Prevents gradient from leaking */
will-change: transform, opacity; /* Optimize for animation performance */ will-change: transform, opacity; /* Optimize for animation performance */
/* Glass morphism effect with backdrop blur */ /* Glass morphism effect with backdrop blur */
backdrop-filter: blur(var(--aurora-blur)); /* Creates frosted glass look */ backdrop-filter: blur(var(--aurora-blur)); /* Creates frosted glass look */
-webkit-backdrop-filter: blur(var(--aurora-blur)); /* Safari support */ -webkit-backdrop-filter: blur(var(--aurora-blur)); /* Safari support */
/** /**
* Gradient overlay * Gradient overlay
* Creates a colored gradient based on notification type * Creates a colored gradient based on notification type
@@ -84,7 +69,6 @@
opacity: 0.8; opacity: 0.8;
border-radius: inherit; /* Match parent's border radius */ border-radius: inherit; /* Match parent's border radius */
} }
/** /**
* Content container * Content container
* Holds message and close button * Holds message and close button
@@ -95,7 +79,6 @@
position: relative; position: relative;
z-index: 1; /* Place above gradient overlay */ z-index: 1; /* Place above gradient overlay */
} }
/** /**
* Message styling * Message styling
* The main notification text * The main notification text
@@ -107,7 +90,6 @@
font-weight: 500; /* Medium weight for better readability */ font-weight: 500; /* Medium weight for better readability */
margin-right: 10px; margin-right: 10px;
} }
/** /**
* Close button styling * Close button styling
* Circular button with hover effect * Circular button with hover effect
@@ -127,13 +109,11 @@
opacity: 0.7; opacity: 0.7;
transition: all 0.2s ease; transition: all 0.2s ease;
color: inherit; color: inherit;
&:hover, &:focus { &:hover, &:focus {
opacity: 1; opacity: 1;
background: rgba(0, 0, 0, 0.1); /* Darker on hover/focus */ background: rgba(0, 0, 0, 0.1); /* Darker on hover/focus */
} }
} }
/** /**
* Progress bar container * Progress bar container
* Holds the animated progress indicator * Holds the animated progress indicator
@@ -149,7 +129,6 @@
opacity: 0.7; /* Slightly transparent */ opacity: 0.7; /* Slightly transparent */
z-index: 1; /* Above gradient, below content */ z-index: 1; /* Above gradient, below content */
} }
/** /**
* Progress indicator * Progress indicator
* Animated by JavaScript to show remaining time * Animated by JavaScript to show remaining time
@@ -158,7 +137,6 @@
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
/** /**
* Type-specific styling * Type-specific styling
* Each notification type gets its own gradient and progress bar color * Each notification type gets its own gradient and progress bar color
@@ -167,39 +145,32 @@
&::before { background: var(--aurora-success-gradient); } &::before { background: var(--aurora-success-gradient); }
.fl-progress { background-color: var(--aurora-success); } .fl-progress { background-color: var(--aurora-success); }
} }
&.fl-info { &.fl-info {
&::before { background: var(--aurora-info-gradient); } &::before { background: var(--aurora-info-gradient); }
.fl-progress { background-color: var(--aurora-info); } .fl-progress { background-color: var(--aurora-info); }
} }
&.fl-warning { &.fl-warning {
&::before { background: var(--aurora-warning-gradient); } &::before { background: var(--aurora-warning-gradient); }
.fl-progress { background-color: var(--aurora-warning); } .fl-progress { background-color: var(--aurora-warning); }
} }
&.fl-error { &.fl-error {
&::before { background: var(--aurora-error-gradient); } &::before { background: var(--aurora-error-gradient); }
.fl-progress { background-color: var(--aurora-error); } .fl-progress { background-color: var(--aurora-error); }
} }
/** /**
* RTL support * RTL support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-message { .fl-message {
margin-right: 0; margin-right: 0;
margin-left: 10px; /* Swap margins */ margin-left: 10px; /* Swap margins */
} }
.fl-progress { .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
@@ -208,8 +179,6 @@
animation: none; /* Disable animation */ animation: none; /* Disable animation */
} }
} }
/**
* Dark mode support * Dark mode support
* Different styling when in dark mode * Different styling when in dark mode
*/ */
@@ -219,16 +188,13 @@ html.fl-dark .fl-aurora,
background-color: var(--aurora-bg-dark); background-color: var(--aurora-bg-dark);
color: var(--aurora-text-dark); color: var(--aurora-text-dark);
box-shadow: var(--aurora-shadow-dark); box-shadow: var(--aurora-shadow-dark);
/* Adjusted close button for dark mode */ /* Adjusted close button for dark mode */
.fl-close { .fl-close {
background: rgba(255, 255, 255, 0.1); /* Lighter background in dark mode */ background: rgba(255, 255, 255, 0.1); /* Lighter background in dark mode */
&:hover, &:focus { &:hover, &:focus {
background: rgba(255, 255, 255, 0.15); /* Lighter hover in dark mode */ background: rgba(255, 255, 255, 0.15); /* Lighter hover in dark mode */
} }
} }
/* Stronger gradients for better visibility in dark mode */ /* Stronger gradients for better visibility in dark mode */
&.fl-success::before { background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(16, 185, 129, 0.25) 100%); } &.fl-success::before { background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(16, 185, 129, 0.25) 100%); }
&.fl-info::before { background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0.25) 100%); } &.fl-info::before { background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0.25) 100%); }
@@ -1,57 +1,10 @@
/**
* @file PHPFlasher Aurora Theme Implementation
* @description Calm, soothing notification style with glass morphism effects
* @author Younes ENNAJI
*/
import './aurora.scss' import './aurora.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Aurora notification theme for PHPFlasher.
*
* The Aurora theme provides an elegant, modern glass-like appearance with:
* - Translucent background with backdrop blur
* - Subtle gradients based on notification type
* - Soft rounded corners and delicate shadows
* - Minimalist design that focuses on content
* - Smooth animation with subtle scaling
* - Accessible structure and interactions
*
* This theme is inspired by modern glass/frosted UI design patterns found in
* iOS and macOS, providing a contemporary feel that works well in both light
* and dark modes.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { auroraTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('aurora', auroraTheme);
*
* // Use the theme
* flasher.use('theme.aurora').success('Your profile has been updated');
* ```
*/
export const auroraTheme = { export const auroraTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a minimal, elegant notification with just the essential elements:
* - Message text
* - Close button
* - Progress indicator
*
* The aurora theme deliberately omits icons to maintain its clean aesthetic,
* relying instead on subtle color gradients to indicate notification type.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,29 +1,4 @@
/**
* @file PHPFlasher Aurora Theme Registration
* @description Registers the Aurora theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { auroraTheme } from './aurora' import { auroraTheme } from './aurora'
/**
* Register the Aurora theme.
*
* This theme provides an elegant glass-morphism notification style with
* subtle gradients and backdrop blur effects.
*
* The registration makes the theme available under the name 'aurora'.
*
* The Aurora theme can be used by calling:
* ```typescript
* flasher.use('theme.aurora').success('Your changes have been saved');
* ```
*
* Key features:
* - Glass-like appearance with blur effects
* - Type-specific gradient backgrounds
* - Minimalist design without icons
* - Smooth entrance animation
* - Progress indicator
*/
flasher.addTheme('aurora', auroraTheme) flasher.addTheme('aurora', auroraTheme)
@@ -1,15 +1,3 @@
/**
* @file PHPFlasher Container Styles
* @description Base styling for individual notification elements
* @author Younes ENNAJI
*/
/**
* .fl-container
*
* The base notification container that all themes build upon.
* Controls core animation behaviors and text color.
*/
.fl-container { .fl-container {
color: var(--text-color, var(--fl-text-light)); color: var(--text-color, var(--fl-text-light));
opacity: 0; opacity: 0;
@@ -17,19 +5,11 @@
transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1); transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
will-change: opacity, transform; will-change: opacity, transform;
/**
* Visible state
* Applied when notification is fully rendered
*/
&.fl-show { &.fl-show {
opacity: 1; opacity: 1;
transform: translate(0, 0) !important; transform: translate(0, 0) !important;
} }
/**
* Right-to-left support
* For languages that read from right to left
*/
&.fl-rtl { &.fl-rtl {
text-align: right; text-align: right;
} }
@@ -1,17 +1,9 @@
/**
* @file PHPFlasher Crystal Theme Styles
* @description CSS styling for the elegant Crystal theme
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Crystal Theme * PHPFlasher - Crystal Theme
* *
* A clean, elegant notification theme with subtle animations * A clean, elegant notification theme with subtle animations
* and a focus on simplicity and readability. * and a focus on simplicity and readability.
*/ */
/**
* Theme-specific variables * Theme-specific variables
* Define the color palette and styling attributes for Crystal theme * Define the color palette and styling attributes for Crystal theme
*/ */
@@ -21,13 +13,10 @@
--crystal-bg-dark: rgba(30, 30, 30, 0.95); /* Near-black in dark mode */ --crystal-bg-dark: rgba(30, 30, 30, 0.95); /* Near-black in dark mode */
--crystal-text-light: #2c3e50; /* Deep blue-gray text for light mode */ --crystal-text-light: #2c3e50; /* Deep blue-gray text for light mode */
--crystal-text-dark: rgba(255, 255, 255, 0.95); /* Off-white text for dark mode */ --crystal-text-dark: rgba(255, 255, 255, 0.95); /* Off-white text for dark mode */
/* Shadow colors for animations and effects */ /* Shadow colors for animations and effects */
--crystal-shadow: rgba(0, 0, 0, 0.08); /* Subtle shadow for light mode */ --crystal-shadow: rgba(0, 0, 0, 0.08); /* Subtle shadow for light mode */
--crystal-shadow-dark: rgba(0, 0, 0, 0.25); /* Stronger shadow for dark mode */ --crystal-shadow-dark: rgba(0, 0, 0, 0.25); /* Stronger shadow for dark mode */
} }
/**
* Entrance animation * Entrance animation
* Slides in from the top with a fade effect * Slides in from the top with a fade effect
*/ */
@@ -41,8 +30,6 @@
transform: translateY(0); /* End at natural position */ transform: translateY(0); /* End at natural position */
} }
} }
/**
* Hover animation * Hover animation
* Creates a subtle pulsing shadow effect * Creates a subtle pulsing shadow effect
*/ */
@@ -57,8 +44,6 @@
box-shadow: 0 2px 8px var(--crystal-shadow); /* Return to normal shadow */ box-shadow: 0 2px 8px var(--crystal-shadow); /* Return to normal shadow */
} }
} }
/**
* Base Crystal theme styling * Base Crystal theme styling
*/ */
.fl-crystal { .fl-crystal {
@@ -71,7 +56,6 @@
border-radius: var(--fl-border-radius, 4px) var(--fl-border-radius, 4px) 0 0; /* Rounded corners */ border-radius: var(--fl-border-radius, 4px) var(--fl-border-radius, 4px) 0 0; /* Rounded corners */
will-change: transform, opacity; /* Optimize for animation performance */ will-change: transform, opacity; /* Optimize for animation performance */
transition: box-shadow 0.3s ease; /* Smooth transition for shadow changes */ transition: box-shadow 0.3s ease; /* Smooth transition for shadow changes */
/** /**
* Hover effect * Hover effect
* Applies pulsing animation when notification is hovered * Applies pulsing animation when notification is hovered
@@ -79,7 +63,6 @@
&:hover { &:hover {
animation: crystalPulse 2s ease-in-out infinite; animation: crystalPulse 2s ease-in-out infinite;
} }
/** /**
* Content container * Content container
* Holds the message and close button * Holds the message and close button
@@ -90,7 +73,6 @@
align-items: center; align-items: center;
gap: 0.75rem; /* Space between elements */ gap: 0.75rem; /* Space between elements */
} }
/** /**
* Text container * Text container
* Holds the message paragraph * Holds the message paragraph
@@ -98,7 +80,6 @@
.fl-text { .fl-text {
flex: 1; /* Take available space */ flex: 1; /* Take available space */
} }
/** /**
* Message styling * Message styling
* The primary notification text * The primary notification text
@@ -109,7 +90,6 @@
line-height: 1.4; /* Improved readability */ line-height: 1.4; /* Improved readability */
color: var(--crystal-text-light, var(--fl-text-light)); color: var(--crystal-text-light, var(--fl-text-light));
} }
/** /**
* Close button styling * Close button styling
* Positioned absolutely to maintain consistent layout * Positioned absolutely to maintain consistent layout
@@ -129,13 +109,11 @@
transition: all 0.2s ease; transition: all 0.2s ease;
color: currentColor; /* Inherit color from parent */ color: currentColor; /* Inherit color from parent */
touch-action: manipulation; /* Better touch behavior */ touch-action: manipulation; /* Better touch behavior */
&:hover, &:focus { &:hover, &:focus {
opacity: 1; /* Full opacity on interaction */ opacity: 1; /* Full opacity on interaction */
transform: translateY(-50%) scale(1.1); /* Slight grow effect */ transform: translateY(-50%) scale(1.1); /* Slight grow effect */
} }
} }
/** /**
* Progress bar container * Progress bar container
* Holds the animated progress indicator * Holds the animated progress indicator
@@ -147,7 +125,6 @@
right: 0; right: 0;
height: 3px; height: 3px;
overflow: hidden; overflow: hidden;
/** /**
* Progress indicator * Progress indicator
* Animated by JavaScript to show remaining time * Animated by JavaScript to show remaining time
@@ -158,7 +135,6 @@
transform-origin: left center; /* Animation starts from left */ transform-origin: left center; /* Animation starts from left */
} }
} }
/** /**
* Type-specific styling * Type-specific styling
* Each notification type gets its own text color * Each notification type gets its own text color
@@ -167,58 +143,47 @@
color: var(--success-color, var(--fl-success)); color: var(--success-color, var(--fl-success));
.fl-message { color: var(--success-color, var(--fl-success)); } .fl-message { color: var(--success-color, var(--fl-success)); }
} }
&.fl-error { &.fl-error {
color: var(--error-color, var(--fl-error)); color: var(--error-color, var(--fl-error));
.fl-message { color: var(--error-color, var(--fl-error)); } .fl-message { color: var(--error-color, var(--fl-error)); }
} }
&.fl-warning { &.fl-warning {
color: var(--warning-color, var(--fl-warning)); color: var(--warning-color, var(--fl-warning));
.fl-message { color: var(--warning-color, var(--fl-warning)); } .fl-message { color: var(--warning-color, var(--fl-warning)); }
} }
&.fl-info { &.fl-info {
color: var(--info-color, var(--fl-info)); color: var(--info-color, var(--fl-info));
.fl-message { color: var(--info-color, var(--fl-info)); } .fl-message { color: var(--info-color, var(--fl-info)); }
} }
/** /**
* RTL support * RTL support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-content { .fl-content {
padding: 1rem 1rem 1rem 2.5rem; /* Swap padding for close button on left */ padding: 1rem 1rem 1rem 2.5rem; /* Swap padding for close button on left */
} }
.fl-close { .fl-close {
right: auto; right: auto;
left: 0.75rem; /* Move close button to left side */ left: 0.75rem; /* Move close button to left side */
} }
.fl-progress .fl-progress { .fl-progress .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
*/ */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
animation: none; /* Disable entrance animation */ animation: none; /* Disable entrance animation */
&:hover { &:hover {
animation: none; /* Disable pulse animation */ animation: none; /* Disable pulse animation */
box-shadow: 0 2px 8px var(--crystal-shadow); /* Keep default shadow */ box-shadow: 0 2px 8px var(--crystal-shadow); /* Keep default shadow */
} }
} }
} }
/**
* Dark mode support * Dark mode support
* Different styling when in dark mode * Different styling when in dark mode
*/ */
@@ -227,11 +192,9 @@ html.fl-dark .fl-crystal,
.fl-crystal.fl-auto-dark { .fl-crystal.fl-auto-dark {
background-color: var(--crystal-bg-dark, var(--fl-bg-dark)); background-color: var(--crystal-bg-dark, var(--fl-bg-dark));
box-shadow: 0 2px 8px var(--crystal-shadow-dark); box-shadow: 0 2px 8px var(--crystal-shadow-dark);
.fl-message { .fl-message {
color: var(--crystal-text-dark, var(--fl-text-dark)); color: var(--crystal-text-dark, var(--fl-text-dark));
} }
&:hover { &:hover {
animation: none; /* Disable pulse in dark mode */ animation: none; /* Disable pulse in dark mode */
box-shadow: 0 4px 16px var(--crystal-shadow-dark); /* Use static enhanced shadow instead */ box-shadow: 0 4px 16px var(--crystal-shadow-dark); /* Use static enhanced shadow instead */
@@ -1,56 +1,10 @@
/**
* @file PHPFlasher Crystal Theme Implementation
* @description Clean, elegant notification theme with subtle animations
* @author Younes ENNAJI
*/
import './crystal.scss' import './crystal.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Crystal notification theme for PHPFlasher.
*
* The Crystal theme provides a clean, elegant design with distinct features:
* - Monochromatic design with colored text for each notification type
* - Subtle entrance animation
* - Gentle pulsing shadow effect on hover
* - Minimalist structure that emphasizes message content
* - Smooth transitions and animations
* - Accessible structure and interactions
*
* This theme aims to be understated yet elegant, providing notifications
* that are noticeable without being distracting.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { crystalTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('crystal', crystalTheme);
*
* // Use the theme
* flasher.use('theme.crystal').success('Document saved successfully');
* ```
*/
export const crystalTheme = { export const crystalTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a clean, elegant notification with minimal elements:
* - Message text
* - Close button
* - Progress indicator
*
* The Crystal theme uses colored text rather than backgrounds to indicate
* notification type, creating a more subtle visual distinction.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,29 +1,4 @@
/**
* @file PHPFlasher Crystal Theme Registration
* @description Registers the Crystal theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { crystalTheme } from './crystal' import { crystalTheme } from './crystal'
/**
* Register the Crystal theme.
*
* This theme provides an elegant notification style with subtle animations
* and a clean, minimal design.
*
* The registration makes the theme available under the name 'crystal'.
*
* The Crystal theme can be used by calling:
* ```typescript
* flasher.use('theme.crystal').success('Your document has been saved');
* ```
*
* Key features:
* - Clean, white background with colored text
* - Subtle pulsing shadow effect on hover
* - Smooth entrance animation
* - Progress indicator
* - Minimalist design that focuses on the message
*/
flasher.addTheme('crystal', crystalTheme) flasher.addTheme('crystal', crystalTheme)
@@ -1,17 +1,9 @@
/**
* @file PHPFlasher Emerald Theme Styles
* @description CSS styling for the elegant glass-like Emerald theme
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Emerald Theme * PHPFlasher - Emerald Theme
* *
* An elegant, minimalist theme with glass-like appearance and bounce animation. * An elegant, minimalist theme with glass-like appearance and bounce animation.
* Features translucent backgrounds with blur effects and colored text. * Features translucent backgrounds with blur effects and colored text.
*/ */
/**
* Theme-specific variables * Theme-specific variables
* Define the color palette and styling attributes for Emerald theme * Define the color palette and styling attributes for Emerald theme
*/ */
@@ -23,15 +15,12 @@
--emerald-text-dark: rgba(255, 255, 255, 0.9); /* Light text for dark mode */ --emerald-text-dark: rgba(255, 255, 255, 0.9); /* Light text for dark mode */
--emerald-shadow: rgba(0, 0, 0, 0.1); /* Subtle shadow */ --emerald-shadow: rgba(0, 0, 0, 0.1); /* Subtle shadow */
--emerald-blur: 8px; /* Backdrop blur amount */ --emerald-blur: 8px; /* Backdrop blur amount */
/* Type-specific colors */ /* Type-specific colors */
--emerald-success: var(--success-color, #16a085); /* Teal green for success */ --emerald-success: var(--success-color, #16a085); /* Teal green for success */
--emerald-error: var(--error-color, #e74c3c); /* Bright red for errors */ --emerald-error: var(--error-color, #e74c3c); /* Bright red for errors */
--emerald-warning: var(--warning-color, #f39c12); /* Orange for warnings */ --emerald-warning: var(--warning-color, #f39c12); /* Orange for warnings */
--emerald-info: var(--info-color, #3498db); /* Blue for information */ --emerald-info: var(--info-color, #3498db); /* Blue for information */
} }
/**
* Entrance animation with bounce effect * Entrance animation with bounce effect
* Combines scaling and vertical movement for a dynamic entrance * Combines scaling and vertical movement for a dynamic entrance
*/ */
@@ -49,8 +38,6 @@
transform: scale(1) translateY(0); /* Settle at final size and position */ transform: scale(1) translateY(0); /* Settle at final size and position */
} }
} }
/**
* Base Emerald theme styling * Base Emerald theme styling
*/ */
.fl-emerald { .fl-emerald {
@@ -66,7 +53,6 @@
padding: 1rem 1.5rem 1rem 1rem; /* Asymmetric padding for close button */ padding: 1rem 1.5rem 1rem 1rem; /* Asymmetric padding for close button */
color: var(--emerald-text-light); /* Default text color */ color: var(--emerald-text-light); /* Default text color */
border-radius: 4px 4px 0 0; /* Rounded corners */ border-radius: 4px 4px 0 0; /* Rounded corners */
/** /**
* Content container * Content container
* Holds message and close button in a flex layout * Holds message and close button in a flex layout
@@ -75,7 +61,6 @@
display: flex; display: flex;
align-items: center; /* Vertically center contents */ align-items: center; /* Vertically center contents */
} }
/** /**
* Message styling * Message styling
* The primary notification text * The primary notification text
@@ -86,7 +71,6 @@
line-height: 1.4; /* Improved readability */ line-height: 1.4; /* Improved readability */
font-weight: 500; /* Medium weight for better visibility */ font-weight: 500; /* Medium weight for better visibility */
} }
/** /**
* Close button styling * Close button styling
* Simple, transparent button with hover effect * Simple, transparent button with hover effect
@@ -100,12 +84,10 @@
opacity: 0.7; /* Subtle appearance by default */ opacity: 0.7; /* Subtle appearance by default */
transition: opacity 0.2s ease; /* Smooth hover effect */ transition: opacity 0.2s ease; /* Smooth hover effect */
color: currentColor; /* Inherit color from parent */ color: currentColor; /* Inherit color from parent */
&:hover, &:focus { &:hover, &:focus {
opacity: 1; /* Full opacity on interaction */ opacity: 1; /* Full opacity on interaction */
} }
} }
/** /**
* Type-specific styling * Type-specific styling
* Each notification type gets its own text color * Each notification type gets its own text color
@@ -113,19 +95,15 @@
&.fl-success { &.fl-success {
color: var(--emerald-success); /* Green text for success */ color: var(--emerald-success); /* Green text for success */
} }
&.fl-error { &.fl-error {
color: var(--emerald-error); /* Red text for errors */ color: var(--emerald-error); /* Red text for errors */
} }
&.fl-warning { &.fl-warning {
color: var(--emerald-warning); /* Orange text for warnings */ color: var(--emerald-warning); /* Orange text for warnings */
} }
&.fl-info { &.fl-info {
color: var(--emerald-info); /* Blue text for info */ color: var(--emerald-info); /* Blue text for info */
} }
/** /**
* RTL support * RTL support
* Right-to-left language direction support * Right-to-left language direction support
@@ -133,17 +111,14 @@
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
padding: 1rem 1rem 1rem 1.5rem; /* Swap padding for RTL */ padding: 1rem 1rem 1rem 1.5rem; /* Swap padding for RTL */
.fl-content { .fl-content {
flex-direction: row-reverse; /* Reverse flex direction */ flex-direction: row-reverse; /* Reverse flex direction */
} }
.fl-close { .fl-close {
margin-left: 0; margin-left: 0;
margin-right: auto; /* Push to the left in RTL */ margin-right: auto; /* Push to the left in RTL */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
@@ -152,8 +127,6 @@
animation: none; /* Disable animation */ animation: none; /* Disable animation */
} }
} }
/**
* Dark mode support * Dark mode support
* Different styling when in dark mode * Different styling when in dark mode
*/ */
@@ -161,7 +134,6 @@ body.fl-dark .fl-emerald,
html.fl-dark .fl-emerald, html.fl-dark .fl-emerald,
.fl-emerald.fl-auto-dark { .fl-emerald.fl-auto-dark {
background: var(--emerald-bg-dark); /* Dark, semi-transparent background */ background: var(--emerald-bg-dark); /* Dark, semi-transparent background */
.fl-message { .fl-message {
color: var(--emerald-text-dark); /* Lighter text in dark mode */ color: var(--emerald-text-dark); /* Lighter text in dark mode */
} }
@@ -1,51 +1,10 @@
/**
* @file PHPFlasher Emerald Theme Implementation
* @description Elegant glass-like notification theme with bounce animation
* @author Younes ENNAJI
*/
import './emerald.scss' import './emerald.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Emerald notification theme for PHPFlasher.
*
* The Emerald theme provides an elegant, glass-like notification style with:
* - Translucent background with backdrop blur effect
* - Distinctive bounce animation on entrance
* - Colored text based on notification type
* - Clean and modern typography
* - Minimalistic design with focus on readability
* - No progress bar for a cleaner appearance
*
* This theme is named "Emerald" for its polished, refined appearance that
* gives notifications a gem-like quality.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { emeraldTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('emerald', emeraldTheme);
*
* // Use the theme
* flasher.use('theme.emerald').success('Your changes have been saved');
* ```
*/
export const emeraldTheme = { export const emeraldTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a minimal, elegant notification without icons or progress bars
* for a clean look. Focuses solely on the message and close button.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,29 +1,4 @@
/**
* @file PHPFlasher Emerald Theme Registration
* @description Registers the Emerald theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { emeraldTheme } from './emerald' import { emeraldTheme } from './emerald'
/**
* Register the Emerald theme.
*
* This theme provides an elegant glass-like notification style with bounce animation
* and translucent background.
*
* The registration makes the theme available under the name 'emerald'.
*
* The Emerald theme can be used by calling:
* ```typescript
* flasher.use('theme.emerald').success('Your changes have been saved');
* ```
*
* Key features:
* - Glass morphism design with backdrop blur
* - Bounce animation on entrance
* - Colored text based on notification type
* - No icons or progress bar for a clean look
* - Clean typography with the Inter font (falls back to system fonts)
*/
flasher.addTheme('emerald', emeraldTheme) flasher.addTheme('emerald', emeraldTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Facebook Theme Styles
* @description CSS styling for Facebook-inspired notifications
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Facebook Theme * PHPFlasher - Facebook Theme
* *
* Classic notifications inspired by Facebook's instantly recognizable interface. * Classic notifications inspired by Facebook's instantly recognizable interface.
@@ -12,7 +6,6 @@
*/ */
.fl-facebook { .fl-facebook {
/* Theme variables - Define the visual appearance of Facebook notifications */ /* Theme variables - Define the visual appearance of Facebook notifications */
/* Base colors for light and dark modes */ /* Base colors for light and dark modes */
--fb-bg-light: #ffffff; /* White background in light mode */ --fb-bg-light: #ffffff; /* White background in light mode */
--fb-bg-dark: #242526; /* Dark gray in dark mode */ --fb-bg-dark: #242526; /* Dark gray in dark mode */
@@ -27,30 +20,24 @@
--fb-blue: #1876f2; /* Facebook's signature blue */ --fb-blue: #1876f2; /* Facebook's signature blue */
--fb-name-color: #050505; /* Username color in light mode */ --fb-name-color: #050505; /* Username color in light mode */
--fb-name-color-dark: #e4e6eb; /* Username color in dark mode */ --fb-name-color-dark: #e4e6eb; /* Username color in dark mode */
/* Type-specific colors for icons */ /* Type-specific colors for icons */
--fb-success: #31a24c; /* Green for success notifications */ --fb-success: #31a24c; /* Green for success notifications */
--fb-info: #1876f2; /* Blue for info notifications */ --fb-info: #1876f2; /* Blue for info notifications */
--fb-warning: #f7b928; /* Yellow for warning notifications */ --fb-warning: #f7b928; /* Yellow for warning notifications */
--fb-error: #e41e3f; /* Red for error notifications */ --fb-error: #e41e3f; /* Red for error notifications */
/* Icon background colors - Light mode */ /* Icon background colors - Light mode */
--fb-success-bg: #e7f3ff; /* Light blue background for success icons */ --fb-success-bg: #e7f3ff; /* Light blue background for success icons */
--fb-info-bg: #e7f3ff; /* Light blue background for info icons */ --fb-info-bg: #e7f3ff; /* Light blue background for info icons */
--fb-warning-bg: #fff5cc; /* Light yellow background for warning icons */ --fb-warning-bg: #fff5cc; /* Light yellow background for warning icons */
--fb-error-bg: #ffebe9; /* Light red background for error icons */ --fb-error-bg: #ffebe9; /* Light red background for error icons */
/* Icon background colors - Dark mode */ /* Icon background colors - Dark mode */
--fb-success-bg-dark: #263c4b; /* Dark blue background for success icons */ --fb-success-bg-dark: #263c4b; /* Dark blue background for success icons */
--fb-info-bg-dark: #263c4b; /* Dark blue background for info icons */ --fb-info-bg-dark: #263c4b; /* Dark blue background for info icons */
--fb-warning-bg-dark: #3e3c26; /* Dark yellow background for warning icons */ --fb-warning-bg-dark: #3e3c26; /* Dark yellow background for warning icons */
--fb-error-bg-dark: #472835; /* Dark red background for error icons */ --fb-error-bg-dark: #472835; /* Dark red background for error icons */
/* Animation timing */ /* Animation timing */
--fb-animation-duration: 0.2s; /* Duration for entrance animation */ --fb-animation-duration: 0.2s; /* Duration for entrance animation */
} }
/**
* Entrance animation for Facebook theme * Entrance animation for Facebook theme
* Slides in from above with a fade effect * Slides in from above with a fade effect
*/ */
@@ -64,8 +51,6 @@
transform: translateY(0); /* End at natural position */ transform: translateY(0); /* End at natural position */
} }
} }
/**
* Base Facebook theme styling * Base Facebook theme styling
*/ */
.fl-facebook { .fl-facebook {
@@ -73,7 +58,6 @@
margin: 8px 0; /* Spacing between notifications */ margin: 8px 0; /* Spacing between notifications */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; /* Facebook's font stack */ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; /* Facebook's font stack */
animation: fbFadeIn var(--fb-animation-duration) ease-out; animation: fbFadeIn var(--fb-animation-duration) ease-out;
/** /**
* Main notification card * Main notification card
* Contains icon, message, and actions * Contains icon, message, and actions
@@ -87,12 +71,10 @@
align-items: flex-start; /* Align items to top for proper icon alignment */ align-items: flex-start; /* Align items to top for proper icon alignment */
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); /* Subtle shadow */ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); /* Subtle shadow */
transition: background-color 0.1s ease; /* Smooth hover transition */ transition: background-color 0.1s ease; /* Smooth hover transition */
&:hover { &:hover {
background-color: var(--fb-hover-light); /* Background change on hover */ background-color: var(--fb-hover-light); /* Background change on hover */
} }
} }
/** /**
* Icon container * Icon container
* Holds the notification type icon * Holds the notification type icon
@@ -101,7 +83,6 @@
margin-right: 12px; margin-right: 12px;
flex-shrink: 0; /* Prevent icon from shrinking */ flex-shrink: 0; /* Prevent icon from shrinking */
} }
/** /**
* Icon styling * Icon styling
* Circular background with centered icon * Circular background with centered icon
@@ -113,12 +94,10 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; /* Center the SVG icon */ justify-content: center; /* Center the SVG icon */
svg { svg {
color: white; /* Icon color is white in light mode */ color: white; /* Icon color is white in light mode */
} }
} }
/** /**
* Type-specific icon styling * Type-specific icon styling
* Each notification type has its own icon color * Each notification type has its own icon color
@@ -126,19 +105,15 @@
.fl-fb-icon-success { .fl-fb-icon-success {
background-color: var(--fb-success); background-color: var(--fb-success);
} }
.fl-fb-icon-info { .fl-fb-icon-info {
background-color: var(--fb-info); background-color: var(--fb-info);
} }
.fl-fb-icon-warning { .fl-fb-icon-warning {
background-color: var(--fb-warning); background-color: var(--fb-warning);
} }
.fl-fb-icon-error { .fl-fb-icon-error {
background-color: var(--fb-error); background-color: var(--fb-error);
} }
/** /**
* Content container * Content container
* Holds message and timestamp * Holds message and timestamp
@@ -147,7 +122,6 @@
flex: 1; /* Take available space */ flex: 1; /* Take available space */
min-width: 0; /* Enable text truncation */ min-width: 0; /* Enable text truncation */
} }
/** /**
* Message styling * Message styling
* Main notification text * Main notification text
@@ -157,7 +131,6 @@
line-height: 1.33; /* Improved readability */ line-height: 1.33; /* Improved readability */
margin-bottom: 4px; margin-bottom: 4px;
} }
/** /**
* Username styling * Username styling
* For displaying user names in bold * For displaying user names in bold
@@ -167,7 +140,6 @@
color: var(--fb-name-color); color: var(--fb-name-color);
margin-right: 4px; margin-right: 4px;
} }
/** /**
* Metadata container * Metadata container
* Holds timestamp and additional info * Holds timestamp and additional info
@@ -176,7 +148,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
} }
/** /**
* Timestamp styling * Timestamp styling
* Shows when the notification was created * Shows when the notification was created
@@ -185,7 +156,6 @@
font-size: 13px; /* Smaller text for timestamp */ font-size: 13px; /* Smaller text for timestamp */
color: var(--fb-text-secondary-light); /* Gray secondary text color */ color: var(--fb-text-secondary-light); /* Gray secondary text color */
} }
/** /**
* Action buttons container * Action buttons container
* Holds interactive buttons like close * Holds interactive buttons like close
@@ -195,7 +165,6 @@
margin-left: 12px; margin-left: 12px;
align-items: center; align-items: center;
} }
/** /**
* Button styling * Button styling
* Circular buttons with hover effect * Circular buttons with hover effect
@@ -213,12 +182,10 @@
color: var(--fb-text-secondary-light); color: var(--fb-text-secondary-light);
margin-left: 8px; margin-left: 8px;
transition: background-color 0.1s; /* Quick hover transition */ transition: background-color 0.1s; /* Quick hover transition */
&:hover { &:hover {
background-color: var(--fb-border-light); /* Darker background on hover */ background-color: var(--fb-border-light); /* Darker background on hover */
} }
} }
/** /**
* Button icon container * Button icon container
* Centers SVG icons within buttons * Centers SVG icons within buttons
@@ -228,35 +195,29 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-icon-container { .fl-icon-container {
margin-right: 0; margin-right: 0;
margin-left: 12px; /* Swap margins for RTL */ margin-left: 12px; /* Swap margins for RTL */
} }
.fl-user-name { .fl-user-name {
margin-right: 0; margin-right: 0;
margin-left: 4px; /* Swap margins for RTL */ margin-left: 4px; /* Swap margins for RTL */
} }
.fl-actions { .fl-actions {
margin-left: 0; margin-left: 0;
margin-right: 12px; /* Swap margins for RTL */ margin-right: 12px; /* Swap margins for RTL */
} }
.fl-button { .fl-button {
margin-left: 0; margin-left: 0;
margin-right: 8px; /* Swap margins for RTL */ margin-right: 8px; /* Swap margins for RTL */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
@@ -265,8 +226,6 @@
animation: none; /* Disable animation */ animation: none; /* Disable animation */
} }
} }
/**
* Dark mode support * Dark mode support
* Different styling when in dark mode * Different styling when in dark mode
*/ */
@@ -277,29 +236,23 @@ html.fl-dark .fl-facebook,
background-color: var(--fb-bg-dark); background-color: var(--fb-bg-dark);
color: var(--fb-text-dark); color: var(--fb-text-dark);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); /* Stronger shadow in dark mode */ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); /* Stronger shadow in dark mode */
&:hover { &:hover {
background-color: var(--fb-hover-dark); background-color: var(--fb-hover-dark);
} }
} }
.fl-user-name { .fl-user-name {
color: var(--fb-name-color-dark); color: var(--fb-name-color-dark);
} }
.fl-time { .fl-time {
color: var(--fb-text-secondary-dark); color: var(--fb-text-secondary-dark);
} }
.fl-button { .fl-button {
background: var(--fb-hover-dark); background: var(--fb-hover-dark);
color: var(--fb-text-secondary-dark); color: var(--fb-text-secondary-dark);
&:hover { &:hover {
background-color: var(--fb-border-dark); background-color: var(--fb-border-dark);
} }
} }
/** /**
* Type-specific icon styling for dark mode * Type-specific icon styling for dark mode
* Uses darker backgrounds with colored icons for better visibility * Uses darker backgrounds with colored icons for better visibility
@@ -308,17 +261,14 @@ html.fl-dark .fl-facebook,
background-color: var(--fb-success-bg-dark); background-color: var(--fb-success-bg-dark);
svg { color: var(--fb-success); } /* Icon color matches type in dark mode */ svg { color: var(--fb-success); } /* Icon color matches type in dark mode */
} }
.fl-fb-icon-info { .fl-fb-icon-info {
background-color: var(--fb-info-bg-dark); background-color: var(--fb-info-bg-dark);
svg { color: var(--fb-info); } svg { color: var(--fb-info); }
} }
.fl-fb-icon-warning { .fl-fb-icon-warning {
background-color: var(--fb-warning-bg-dark); background-color: var(--fb-warning-bg-dark);
svg { color: var(--fb-warning); } svg { color: var(--fb-warning); }
} }
.fl-fb-icon-error { .fl-fb-icon-error {
background-color: var(--fb-error-bg-dark); background-color: var(--fb-error-bg-dark);
svg { color: var(--fb-error); } svg { color: var(--fb-error); }
@@ -1,65 +1,17 @@
/**
* @file PHPFlasher Facebook Theme Implementation
* @description Social media style notifications inspired by Facebook's interface
* @author Younes ENNAJI
*/
import './facebook.scss' import './facebook.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Facebook-inspired notification theme for PHPFlasher.
*
* This theme replicates the familiar notification style from Facebook's interface,
* featuring:
* - Rounded notification cards with subtle shadows
* - Circular icons with type-specific colors
* - Message content with time information
* - Close button with hover effect
* - Facebook's signature font and color scheme
*
* The theme is designed to be immediately recognizable to users familiar with
* the Facebook platform, providing a sense of familiarity and consistency.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { facebookTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('facebook', facebookTheme);
*
* // Use the theme
* flasher.use('theme.facebook').success('Your post was published successfully');
* ```
*/
export const facebookTheme = { export const facebookTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a Facebook-style notification with icon, message, timestamp, and close button.
* The layout mimics Facebook's notification design for a familiar user experience.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
// Format current time in Facebook style (hour:minute)
const now = new Date() const now = new Date()
const timeString = now.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }) const timeString = now.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
/**
* Gets the appropriate icon based on notification type.
* Each type has a specific icon matching Facebook's visual language.
*
* @returns SVG markup for the notification icon
*/
const getNotificationIcon = () => { const getNotificationIcon = () => {
switch (type) { switch (type) {
case 'success': case 'success':
@@ -108,7 +60,7 @@ export const facebookTheme = {
<button class="fl-button fl-close" aria-label="Close ${type} message"> <button class="fl-button fl-close" aria-label="Close ${type} message">
<div class="fl-button-icon"> <div class="fl-button-icon">
<svg viewBox="0 0 24 24" width="20" height="20"> <svg viewBox="0 0 24 24" width="20" height="20">
<path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/> <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
</svg> </svg>
</div> </div>
</button> </button>
@@ -1,27 +1,4 @@
/**
* @file PHPFlasher Facebook Theme Registration
* @description Registers the Facebook theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { facebookTheme } from './facebook' import { facebookTheme } from './facebook'
/**
* Register the Facebook theme.
*
* This theme provides notifications styled after Facebook's interface design.
* The registration makes the theme available under the name 'facebook'.
*
* The Facebook theme can be used by calling:
* ```typescript
* flasher.use('theme.facebook').success('Your post was published successfully');
* ```
*
* Key features:
* - Facebook-like notification cards with rounded corners and subtle shadows
* - Type-specific circular icons with branded colors
* - Timestamp display similar to Facebook's time format
* - Interactive elements with hover states
* - Facebook's distinctive font family and color palette
*/
flasher.addTheme('facebook', facebookTheme) flasher.addTheme('facebook', facebookTheme)
@@ -1,49 +1,14 @@
/**
* @file PHPFlasher Default Theme
* @description Theme implementation for the default PHPFlasher notification style
* @author Younes ENNAJI
*/
import './flasher.scss' import './flasher.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* The default PHPFlasher theme.
*
* This theme provides a classic bordered notification style with:
* - Colored left border based on notification type
* - Icon representing the notification type
* - Title and message content
* - Close button
* - Progress bar for auto-dismiss
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { flasherTheme } from '@flasher/flasher/themes';
*
* // Register the theme if not already registered
* flasher.addTheme('custom-name', flasherTheme);
*
* // Use the theme
* flasher.use('theme.custom-name').success('Operation completed');
* ```
*/
export const flasherTheme = { export const flasherTheme = {
/**
* Renders an envelope as HTML.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, title, message } = envelope const { type, title, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
// Use provided title or capitalize the notification type
const displayTitle = title || type.charAt(0).toUpperCase() + type.slice(1) const displayTitle = title || type.charAt(0).toUpperCase() + type.slice(1)
return ` return `
@@ -1,20 +1,4 @@
/**
* @file PHPFlasher Default Theme Registration
* @description Registers the default theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { flasherTheme } from './flasher' import { flasherTheme } from './flasher'
/**
* Register the default "flasher" theme.
*
* This theme is automatically registered when PHPFlasher is initialized
* and is used as the default theme when no specific theme is requested.
*
* The flasher theme is used whenever:
* - You call flasher.success(), flasher.error(), etc. directly
* - You use flasher.use('flasher') explicitly
* - You use flasher.use('theme.flasher') explicitly
*/
flasher.addTheme('flasher', flasherTheme) flasher.addTheme('flasher', flasherTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Google Theme Styles
* @description CSS styling for Material Design-inspired notifications
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Google Theme * PHPFlasher - Google Theme
* *
* Google's Material Design-inspired notifications. * Google's Material Design-inspired notifications.
@@ -12,7 +6,6 @@
*/ */
.fl-google { .fl-google {
/* Theme variables - Define the visual appearance of Material Design notifications */ /* Theme variables - Define the visual appearance of Material Design notifications */
/* Base colors and appearance for light and dark modes */ /* Base colors and appearance for light and dark modes */
--md-bg-light: #ffffff; /* Card background in light mode */ --md-bg-light: #ffffff; /* Card background in light mode */
--md-bg-dark: #2d2d2d; /* Card background in dark mode */ --md-bg-dark: #2d2d2d; /* Card background in dark mode */
@@ -20,29 +13,23 @@
--md-text-secondary-light: rgba(0, 0, 0, 0.6); /* Secondary text in light mode (60% black) */ --md-text-secondary-light: rgba(0, 0, 0, 0.6); /* Secondary text in light mode (60% black) */
--md-text-dark: rgba(255, 255, 255, 0.87); /* Primary text in dark mode (87% white) */ --md-text-dark: rgba(255, 255, 255, 0.87); /* Primary text in dark mode (87% white) */
--md-text-secondary-dark: rgba(255, 255, 255, 0.6); /* Secondary text in dark mode (60% white) */ --md-text-secondary-dark: rgba(255, 255, 255, 0.6); /* Secondary text in dark mode (60% white) */
/* Material Design elevation - multi-layered shadows */ /* Material Design elevation - multi-layered shadows */
--md-elevation: 0 3px 5px -1px rgba(0,0,0,0.2), /* Ambient shadow */ --md-elevation: 0 3px 5px -1px rgba(0,0,0,0.2), /* Ambient shadow */
0 6px 10px 0 rgba(0,0,0,0.14), /* Penumbra shadow */ 0 6px 10px 0 rgba(0,0,0,0.14), /* Penumbra shadow */
0 1px 18px 0 rgba(0,0,0,0.12); /* Umbra shadow */ 0 1px 18px 0 rgba(0,0,0,0.12); /* Umbra shadow */
--md-elevation-dark: 0 3px 5px -1px rgba(0,0,0,0.4), /* Darker ambient shadow */ --md-elevation-dark: 0 3px 5px -1px rgba(0,0,0,0.4), /* Darker ambient shadow */
0 6px 10px 0 rgba(0,0,0,0.28), /* Darker penumbra shadow */ 0 6px 10px 0 rgba(0,0,0,0.28), /* Darker penumbra shadow */
0 1px 18px 0 rgba(0,0,0,0.24); /* Darker umbra shadow */ 0 1px 18px 0 rgba(0,0,0,0.24); /* Darker umbra shadow */
--md-border-radius: 4px; /* Material Design default corner radius */ --md-border-radius: 4px; /* Material Design default corner radius */
/* Material Design color palette for notification types */ /* Material Design color palette for notification types */
--md-success: #43a047; /* Green 600 from Material palette */ --md-success: #43a047; /* Green 600 from Material palette */
--md-info: #1e88e5; /* Blue 600 from Material palette */ --md-info: #1e88e5; /* Blue 600 from Material palette */
--md-warning: #fb8c00; /* Orange 600 from Material palette */ --md-warning: #fb8c00; /* Orange 600 from Material palette */
--md-error: #e53935; /* Red 600 from Material palette */ --md-error: #e53935; /* Red 600 from Material palette */
/* Animation timing variables */ /* Animation timing variables */
--md-animation-duration: 0.3s; /* Standard Material motion duration */ --md-animation-duration: 0.3s; /* Standard Material motion duration */
--md-ripple-duration: 0.6s; /* Ripple effect duration */ --md-ripple-duration: 0.6s; /* Ripple effect duration */
} }
/**
* Entrance animation for Google theme * Entrance animation for Google theme
* Material Design-style slide up with easing * Material Design-style slide up with easing
*/ */
@@ -56,8 +43,6 @@
transform: translateY(0); /* End at final position */ transform: translateY(0); /* End at final position */
} }
} }
/**
* Material Design ripple effect animation * Material Design ripple effect animation
* Creates expanding circle from point of interaction * Creates expanding circle from point of interaction
*/ */
@@ -67,8 +52,6 @@
opacity: 0; /* Fade out completely */ opacity: 0; /* Fade out completely */
} }
} }
/**
* Base Google Material Design theme styling * Base Google Material Design theme styling
*/ */
.fl-google { .fl-google {
@@ -76,7 +59,6 @@
margin: 8px 0; /* Spacing between notifications */ margin: 8px 0; /* Spacing between notifications */
font-family: Roboto, "Segoe UI", Helvetica, Arial, sans-serif; /* Material uses Roboto */ font-family: Roboto, "Segoe UI", Helvetica, Arial, sans-serif; /* Material uses Roboto */
animation: mdSlideUp var(--md-animation-duration) cubic-bezier(0.4, 0, 0.2, 1); /* Material standard easing */ animation: mdSlideUp var(--md-animation-duration) cubic-bezier(0.4, 0, 0.2, 1); /* Material standard easing */
/** /**
* Main card container * Main card container
* Follows Material Design card component styling * Follows Material Design card component styling
@@ -88,7 +70,6 @@
box-shadow: var(--md-elevation); /* Multi-layered shadow for depth */ box-shadow: var(--md-elevation); /* Multi-layered shadow for depth */
overflow: hidden; /* Contains progress bar */ overflow: hidden; /* Contains progress bar */
} }
/** /**
* Content section * Content section
* Contains icon and text content * Contains icon and text content
@@ -98,7 +79,6 @@
display: flex; display: flex;
align-items: flex-start; /* Align items to top */ align-items: flex-start; /* Align items to top */
} }
/** /**
* Icon container * Icon container
* Holds the Material Design SVG icon * Holds the Material Design SVG icon
@@ -108,7 +88,6 @@
color: var(--md-text-secondary-light); /* Default icon color */ color: var(--md-text-secondary-light); /* Default icon color */
flex-shrink: 0; /* Prevent icon from shrinking */ flex-shrink: 0; /* Prevent icon from shrinking */
} }
/** /**
* Text content container * Text content container
* Holds title and message * Holds title and message
@@ -116,7 +95,6 @@
.fl-text-content { .fl-text-content {
flex: 1; /* Take available space */ flex: 1; /* Take available space */
} }
/** /**
* Title styling * Title styling
* Following Material Design typography * Following Material Design typography
@@ -126,7 +104,6 @@
font-weight: 500; /* Medium weight per Material guidelines */ font-weight: 500; /* Medium weight per Material guidelines */
margin-bottom: 4px; margin-bottom: 4px;
} }
/** /**
* Message styling * Message styling
* Following Material Design typography * Following Material Design typography
@@ -136,7 +113,6 @@
line-height: 1.43; /* Material line height for body 2 */ line-height: 1.43; /* Material line height for body 2 */
color: var(--md-text-secondary-light); /* Secondary text color (60% opacity) */ color: var(--md-text-secondary-light); /* Secondary text color (60% opacity) */
} }
/** /**
* Action buttons section * Action buttons section
* Contains dismiss button * Contains dismiss button
@@ -146,7 +122,6 @@
justify-content: flex-end; /* Align buttons to right */ justify-content: flex-end; /* Align buttons to right */
padding: 8px; /* Standard Material padding */ padding: 8px; /* Standard Material padding */
} }
/** /**
* Action button styling * Action button styling
* Following Material Design "text button" guidelines * Following Material Design "text button" guidelines
@@ -166,11 +141,9 @@
transition: background-color 0.2s; /* Smooth hover effect */ transition: background-color 0.2s; /* Smooth hover effect */
position: relative; /* For ripple positioning */ position: relative; /* For ripple positioning */
overflow: hidden; /* Contain ripple effect */ overflow: hidden; /* Contain ripple effect */
&:hover, &:focus { &:hover, &:focus {
background-color: rgba(0, 0, 0, 0.04); /* Material hover state - 4% opacity */ background-color: rgba(0, 0, 0, 0.04); /* Material hover state - 4% opacity */
} }
/** /**
* Ripple effect * Ripple effect
* Material Design's signature ink ripple on interaction * Material Design's signature ink ripple on interaction
@@ -186,13 +159,11 @@
transform: scale(1); transform: scale(1);
pointer-events: none; /* Don't interfere with clicks */ pointer-events: none; /* Don't interfere with clicks */
} }
&:active::after { &:active::after {
opacity: 0.3; /* Material ripple opacity */ opacity: 0.3; /* Material ripple opacity */
animation: mdRipple var(--md-ripple-duration) linear; /* Expand and fade */ animation: mdRipple var(--md-ripple-duration) linear; /* Expand and fade */
} }
} }
/** /**
* Type-specific styling * Type-specific styling
* Each notification type has its own color based on Material palette * Each notification type has its own color based on Material palette
@@ -201,42 +172,34 @@
.fl-icon-wrapper { .fl-icon-wrapper {
color: var(--md-success); /* Green icon */ color: var(--md-success); /* Green icon */
} }
.fl-action-button { .fl-action-button {
color: var(--md-success); /* Green button */ color: var(--md-success); /* Green button */
} }
} }
&.fl-info { &.fl-info {
.fl-icon-wrapper { .fl-icon-wrapper {
color: var(--md-info); /* Blue icon */ color: var(--md-info); /* Blue icon */
} }
.fl-action-button { .fl-action-button {
color: var(--md-info); /* Blue button */ color: var(--md-info); /* Blue button */
} }
} }
&.fl-warning { &.fl-warning {
.fl-icon-wrapper { .fl-icon-wrapper {
color: var(--md-warning); /* Orange icon */ color: var(--md-warning); /* Orange icon */
} }
.fl-action-button { .fl-action-button {
color: var(--md-warning); /* Orange button */ color: var(--md-warning); /* Orange button */
} }
} }
&.fl-error { &.fl-error {
.fl-icon-wrapper { .fl-icon-wrapper {
color: var(--md-error); /* Red icon */ color: var(--md-error); /* Red icon */
} }
.fl-action-button { .fl-action-button {
color: var(--md-error); /* Red button */ color: var(--md-error); /* Red button */
} }
} }
/** /**
* Progress bar * Progress bar
* Material Design linear progress indicator * Material Design linear progress indicator
@@ -248,14 +211,12 @@
right: 0; right: 0;
height: 4px; /* Material's standard progress height */ height: 4px; /* Material's standard progress height */
overflow: hidden; overflow: hidden;
.fl-progress { .fl-progress {
height: 100%; height: 100%;
width: 100%; width: 100%;
transform-origin: left center; /* Animation starts from left */ transform-origin: left center; /* Animation starts from left */
} }
} }
/** /**
* Type-specific progress colors * Type-specific progress colors
* Each notification type has its own progress bar color * Each notification type has its own progress bar color
@@ -263,58 +224,46 @@
&.fl-success .fl-progress { &.fl-success .fl-progress {
background-color: var(--md-success); /* Green progress */ background-color: var(--md-success); /* Green progress */
} }
&.fl-info .fl-progress { &.fl-info .fl-progress {
background-color: var(--md-info); /* Blue progress */ background-color: var(--md-info); /* Blue progress */
} }
&.fl-warning .fl-progress { &.fl-warning .fl-progress {
background-color: var(--md-warning); /* Orange progress */ background-color: var(--md-warning); /* Orange progress */
} }
&.fl-error .fl-progress { &.fl-error .fl-progress {
background-color: var(--md-error); /* Red progress */ background-color: var(--md-error); /* Red progress */
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-content { .fl-content {
flex-direction: row-reverse; /* Reverse flex direction */ flex-direction: row-reverse; /* Reverse flex direction */
} }
.fl-icon-wrapper { .fl-icon-wrapper {
margin-right: 0; margin-right: 0;
margin-left: 16px; /* Swap margins for RTL */ margin-left: 16px; /* Swap margins for RTL */
} }
.fl-actions { .fl-actions {
justify-content: flex-start; /* Align buttons to left in RTL */ justify-content: flex-start; /* Align buttons to left in RTL */
} }
.fl-progress { .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
*/ */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
animation: none; /* Disable slide animation */ animation: none; /* Disable slide animation */
.fl-action-button:active::after { .fl-action-button:active::after {
animation: none; /* Disable ripple animation */ animation: none; /* Disable ripple animation */
} }
} }
} }
/**
* Dark mode support * Dark mode support
* Material Design dark theme implementation * Material Design dark theme implementation
*/ */
@@ -326,11 +275,9 @@ html.fl-dark .fl-google,
color: var(--md-text-dark); /* Lighter text */ color: var(--md-text-dark); /* Lighter text */
box-shadow: var(--md-elevation-dark); /* Stronger shadows */ box-shadow: var(--md-elevation-dark); /* Stronger shadows */
} }
.fl-message { .fl-message {
color: var(--md-text-secondary-dark); /* Lighter secondary text */ color: var(--md-text-secondary-dark); /* Lighter secondary text */
} }
.fl-action-button { .fl-action-button {
&:hover, &:focus { &:hover, &:focus {
background-color: rgba(255, 255, 255, 0.08); /* Material dark hover - 8% opacity */ background-color: rgba(255, 255, 255, 0.08); /* Material dark hover - 8% opacity */
@@ -1,65 +1,16 @@
/**
* @file PHPFlasher Google Theme Implementation
* @description Material Design-inspired notification theme
* @author Younes ENNAJI
*/
import './google.scss' import './google.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Google Material Design-inspired notification theme for PHPFlasher.
*
* This theme replicates Google's Material Design aesthetics with:
* - Elevated card component with proper shadow depth
* - Material Design iconography
* - Google's typography system with Roboto font
* - Material "ink ripple" effect on buttons
* - UPPERCASE action buttons following Material guidelines
* - Smooth animation with Material Design's motion patterns
*
* The theme follows Material Design specifications for components like
* cards, buttons, typography and iconography, creating a familiar experience
* for users of Google products.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { googleTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('google', googleTheme);
*
* // Use the theme
* flasher.use('theme.google').success('Operation completed successfully');
* ```
*/
export const googleTheme = { export const googleTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a Google Material Design-style notification with icon, title (optional),
* message, dismissal button, and progress indicator.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message, title } = envelope const { type, message, title } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
// Action button text in Material Design style (uppercase)
const actionText = 'DISMISS' const actionText = 'DISMISS'
/**
* Gets the appropriate Material Design icon based on notification type.
* Each icon follows Google's Material Design iconography guidelines.
*
* @returns SVG markup for the notification icon
*/
const getIcon = () => { const getIcon = () => {
switch (type) { switch (type) {
case 'success': case 'success':
@@ -82,7 +33,6 @@ export const googleTheme = {
return '' return ''
} }
// Generate title section if title is provided
const titleSection = title ? `<div class="fl-title">${title}</div>` : '' const titleSection = title ? `<div class="fl-title">${title}</div>` : ''
return ` return `
@@ -1,28 +1,4 @@
/**
* @file PHPFlasher Google Theme Registration
* @description Registers the Google theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { googleTheme } from './google' import { googleTheme } from './google'
/**
* Register the Google theme.
*
* This theme provides notifications styled after Google's Material Design system.
* The registration makes the theme available under the name 'google'.
*
* The Google theme can be used by calling:
* ```typescript
* flasher.use('theme.google').success('Operation completed successfully');
* ```
*
* Key features:
* - Material Design card with proper elevation
* - Google's typography system with Roboto font
* - Material Design icons and color palette
* - Ripple effect on interaction
* - UPPERCASE action button text
* - Proper implementation of Material Design light and dark themes
*/
flasher.addTheme('google', googleTheme) flasher.addTheme('google', googleTheme)
@@ -1,13 +1,3 @@
/**
* @file PHPFlasher Icon Styles
* @description Icon styling for different notification types
* @author Younes ENNAJI
*/
/**
* Base icon styling
* Shared properties for all notification icons
*/
.fl-icon { .fl-icon {
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
@@ -31,10 +21,6 @@
} }
} }
/**
* Success checkmark icon
* Creates a checkmark (✓) symbol using CSS
*/
.fl-success .fl-icon { .fl-success .fl-icon {
&::before, &::before,
&::after { &::after {
@@ -54,10 +40,6 @@
} }
} }
/**
* Info icon
* Creates an information (i) symbol using CSS
*/
.fl-info .fl-icon { .fl-info .fl-icon {
&::before, &::before,
&::after { &::after {
@@ -80,10 +62,6 @@
} }
} }
/**
* Warning icon
* Creates an exclamation (!) symbol using CSS
*/
.fl-warning .fl-icon { .fl-warning .fl-icon {
&::before, &::before,
&::after { &::after {
@@ -105,10 +83,6 @@
} }
} }
/**
* Error X icon
* Creates an X symbol using CSS
*/
.fl-error .fl-icon { .fl-error .fl-icon {
&::before, &::before,
&::after { &::after {
@@ -126,10 +100,6 @@
} }
} }
/**
* Icon background colors
* Sets the background color for each notification type's icon
*/
@each $type in success, info, warning, error { @each $type in success, info, warning, error {
.fl-#{$type} .fl-icon { .fl-#{$type} .fl-icon {
background-color: var(--#{$type}-color, var(--fl-#{$type})); background-color: var(--#{$type}-color, var(--fl-#{$type}));
+4 -22
View File
@@ -1,32 +1,17 @@
/**
* @file PHPFlasher Core Theme Styles
* @description Root styling and CSS variables for the PHPFlasher notification system
* @author Younes ENNAJI
*/
@use "sass:color"; @use "sass:color";
@use "sass:meta"; @use "sass:meta";
/**
* PHPFlasher CSS Variable System
*
* This defines the design token system used throughout all themes.
* By changing these variables, you can customize the appearance of all themes.
*/
:root { :root {
// State Colors - Used for different notification types --fl-success: #10b981;
--fl-success: #10b981; // Green for success messages --fl-info: #3b82f6;
--fl-info: #3b82f6; // Blue for information messages --fl-warning: #f59e0b;
--fl-warning: #f59e0b; // Amber for warning messages --fl-error: #ef4444;
--fl-error: #ef4444; // Red for error messages
// Light variants - Used for backgrounds and progress bars
--fl-success-light: color-mix(in srgb, var(--fl-success) 10%, transparent); --fl-success-light: color-mix(in srgb, var(--fl-success) 10%, transparent);
--fl-info-light: color-mix(in srgb, var(--fl-info) 10%, transparent); --fl-info-light: color-mix(in srgb, var(--fl-info) 10%, transparent);
--fl-warning-light: color-mix(in srgb, var(--fl-warning) 10%, transparent); --fl-warning-light: color-mix(in srgb, var(--fl-warning) 10%, transparent);
--fl-error-light: color-mix(in srgb, var(--fl-error) 10%, transparent); --fl-error-light: color-mix(in srgb, var(--fl-error) 10%, transparent);
// Base colors - Used for text and backgrounds
--fl-white: #ffffff; --fl-white: #ffffff;
--fl-black: #000000; --fl-black: #000000;
--fl-bg-light: var(--fl-white); --fl-bg-light: var(--fl-white);
@@ -34,14 +19,12 @@
--fl-text-light: rgb(75, 85, 99); --fl-text-light: rgb(75, 85, 99);
--fl-text-dark: var(--fl-white); --fl-text-dark: var(--fl-white);
// Appearance - Used for consistent styling across themes
--fl-font: system-ui, -apple-system, BlinkMacSystemFont, sans-serif; --fl-font: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
--fl-border-radius: 4px; --fl-border-radius: 4px;
--fl-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); --fl-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
--fl-shadow-dark: 0 4px 12px rgba(0, 0, 0, 0.35); --fl-shadow-dark: 0 4px 12px rgba(0, 0, 0, 0.35);
--fl-transition: 0.4s cubic-bezier(0.23, 1, 0.32, 1); --fl-transition: 0.4s cubic-bezier(0.23, 1, 0.32, 1);
// Legacy Support - For backward compatibility
--background-color: var(--fl-bg-light); --background-color: var(--fl-bg-light);
--text-color: var(--fl-text-light); --text-color: var(--fl-text-light);
--dark-background-color: var(--fl-bg-dark); --dark-background-color: var(--fl-bg-dark);
@@ -56,7 +39,6 @@
--error-color-light: var(--fl-error-light); --error-color-light: var(--fl-error-light);
} }
// Import component styles
@include meta.load-css("wrapper"); @include meta.load-css("wrapper");
@include meta.load-css("container"); @include meta.load-css("container");
@include meta.load-css("progress"); @include meta.load-css("progress");
@@ -1,24 +1,3 @@
/**
* @file PHPFlasher Theme Exports
* @description Exports all available notification themes
* @author Younes ENNAJI
*/
/**
* Export all available themes for PHPFlasher.
*
* These themes provide different visual styles for notifications and can be
* used by specifying the theme name when showing a notification:
*
* @example
* ```typescript
* // Use a specific theme
* flasher.use('theme.material').success('Operation completed');
*
* // Set a theme as default
* flasher.addPlugin('flasher', flasher.use('theme.slack'));
* ```
*/
export { amazonTheme } from './amazon/amazon' export { amazonTheme } from './amazon/amazon'
export { amberTheme } from './amber/amber' export { amberTheme } from './amber/amber'
export { auroraTheme } from './aurora/aurora' export { auroraTheme } from './aurora/aurora'
@@ -1,28 +1,4 @@
/**
* @file PHPFlasher iOS Theme Registration
* @description Registers the iOS theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { iosTheme } from './ios' import { iosTheme } from './ios'
/**
* Register the iOS theme.
*
* This theme provides notifications styled after Apple's iOS notification system.
* The registration makes the theme available under the name 'ios'.
*
* The iOS theme can be used by calling:
* ```typescript
* flasher.use('theme.ios').success('Your file was uploaded successfully');
* ```
*
* Key features:
* - Frosted glass effect with backdrop blur
* - App icon and name in header
* - Time display in iOS style
* - Subtle animations mimicking iOS notification behavior
* - iOS-style close button
* - Full dark mode support following iOS dark appearance
*/
flasher.addTheme('ios', iosTheme) flasher.addTheme('ios', iosTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher iOS Theme Styles
* @description CSS styling for Apple iOS-inspired notifications
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - iOS Theme * PHPFlasher - iOS Theme
* *
* Clean, sleek notifications inspired by Apple's iOS design language. * Clean, sleek notifications inspired by Apple's iOS design language.
@@ -12,7 +6,6 @@
*/ */
.fl-ios { .fl-ios {
/* Theme variables - Define the visual appearance of iOS notifications */ /* Theme variables - Define the visual appearance of iOS notifications */
/* Base colors and appearance */ /* Base colors and appearance */
--ios-bg-light: rgba(255, 255, 255); /* Semi-transparent white in light mode */ --ios-bg-light: rgba(255, 255, 255); /* Semi-transparent white in light mode */
--ios-bg-dark: rgba(30, 30, 30); /* Semi-transparent dark in dark mode */ --ios-bg-dark: rgba(30, 30, 30); /* Semi-transparent dark in dark mode */
@@ -25,18 +18,14 @@
--ios-shadow-dark: 0 2px 12px rgba(0, 0, 0, 0.35); /* Darker shadow in dark mode */ --ios-shadow-dark: 0 2px 12px rgba(0, 0, 0, 0.35); /* Darker shadow in dark mode */
--ios-icon-size: 18px; /* Size for icons */ --ios-icon-size: 18px; /* Size for icons */
--ios-blur: 30px; /* Amount of backdrop blur for glass effect */ --ios-blur: 30px; /* Amount of backdrop blur for glass effect */
/* Type-specific colors following iOS palette */ /* Type-specific colors following iOS palette */
--ios-success: #34c759; /* iOS green */ --ios-success: #34c759; /* iOS green */
--ios-info: #007aff; /* iOS blue */ --ios-info: #007aff; /* iOS blue */
--ios-warning: #ff9500; /* iOS orange */ --ios-warning: #ff9500; /* iOS orange */
--ios-error: #ff3b30; /* iOS red */ --ios-error: #ff3b30; /* iOS red */
/* Animation timing */ /* Animation timing */
--ios-animation-duration: 0.4s; /* Duration for entrance animation */ --ios-animation-duration: 0.4s; /* Duration for entrance animation */
} }
/**
* Slide-in entrance animation for iOS theme * Slide-in entrance animation for iOS theme
* Combines movement, scaling, and fade for realistic iOS appearance * Combines movement, scaling, and fade for realistic iOS appearance
*/ */
@@ -50,8 +39,6 @@
transform: translateY(0) scale(1); /* End at normal size and position */ transform: translateY(0) scale(1); /* End at normal size and position */
} }
} }
/**
* Content expansion animation * Content expansion animation
* Creates a subtle unfold effect for the notification content * Creates a subtle unfold effect for the notification content
*/ */
@@ -65,8 +52,6 @@
opacity: 1; /* Fade in */ opacity: 1; /* Fade in */
} }
} }
/**
* Base iOS theme styling * Base iOS theme styling
*/ */
.fl-ios { .fl-ios {
@@ -75,7 +60,6 @@
font-family: -apple-system, BlinkMacSystemFont, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif; /* Apple system fonts */ font-family: -apple-system, BlinkMacSystemFont, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif; /* Apple system fonts */
animation: iosSlideIn var(--ios-animation-duration) cubic-bezier(0.23, 1, 0.32, 1); /* iOS-like easing */ animation: iosSlideIn var(--ios-animation-duration) cubic-bezier(0.23, 1, 0.32, 1); /* iOS-like easing */
will-change: transform, opacity; /* Optimize for animation performance */ will-change: transform, opacity; /* Optimize for animation performance */
/** /**
* Main notification container * Main notification container
* Replicates iOS notification card with frosted glass effect * Replicates iOS notification card with frosted glass effect
@@ -87,12 +71,10 @@
box-shadow: var(--ios-shadow); box-shadow: var(--ios-shadow);
padding: 12px 15px; padding: 12px 15px;
position: relative; position: relative;
/* iOS-typical frosted glass effect */ /* iOS-typical frosted glass effect */
backdrop-filter: blur(var(--ios-blur)); backdrop-filter: blur(var(--ios-blur));
-webkit-backdrop-filter: blur(var(--ios-blur)); -webkit-backdrop-filter: blur(var(--ios-blur));
} }
/** /**
* Header section * Header section
* Contains app icon, app name, and timestamp * Contains app icon, app name, and timestamp
@@ -103,7 +85,6 @@
margin-bottom: 8px; margin-bottom: 8px;
padding-right: 20px; /* Space for close button */ padding-right: 20px; /* Space for close button */
} }
/** /**
* App icon container * App icon container
* Square with rounded corners like iOS app icons * Square with rounded corners like iOS app icons
@@ -119,7 +100,6 @@
justify-content: center; justify-content: center;
flex-shrink: 0; /* Prevent icon from shrinking */ flex-shrink: 0; /* Prevent icon from shrinking */
} }
/** /**
* SVG icon styling * SVG icon styling
* White icon on colored background * White icon on colored background
@@ -129,7 +109,6 @@
width: 14px; width: 14px;
height: 14px; height: 14px;
} }
/** /**
* App info container * App info container
* Holds app name and timestamp * Holds app name and timestamp
@@ -140,7 +119,6 @@
justify-content: space-between; justify-content: space-between;
flex: 1; flex: 1;
} }
/** /**
* App name styling * App name styling
* Bold text like in iOS notifications * Bold text like in iOS notifications
@@ -149,7 +127,6 @@
font-weight: 600; /* Semi-bold like iOS */ font-weight: 600; /* Semi-bold like iOS */
font-size: 0.85rem; /* iOS app name size */ font-size: 0.85rem; /* iOS app name size */
} }
/** /**
* Time display * Time display
* Shows current time in iOS style * Shows current time in iOS style
@@ -160,7 +137,6 @@
margin-left: 5px; margin-left: 5px;
flex-shrink: 0; /* Prevent time from wrapping */ flex-shrink: 0; /* Prevent time from wrapping */
} }
/** /**
* Content/message area * Content/message area
* Contains the notification message * Contains the notification message
@@ -170,7 +146,6 @@
animation-delay: 0.1s; /* Slight delay for sequenced animation */ animation-delay: 0.1s; /* Slight delay for sequenced animation */
overflow: hidden; /* Contain expansion animation */ overflow: hidden; /* Contain expansion animation */
} }
/** /**
* Message styling * Message styling
* Main notification text * Main notification text
@@ -181,7 +156,6 @@
margin: 0; margin: 0;
padding-right: 15px; /* Space for close button */ padding-right: 15px; /* Space for close button */
} }
/** /**
* Close button styling * Close button styling
* Circular close button like in iOS * Circular close button like in iOS
@@ -205,12 +179,10 @@
cursor: pointer; cursor: pointer;
transition: opacity 0.2s; transition: opacity 0.2s;
padding: 0; padding: 0;
&:hover, &:focus { &:hover, &:focus {
opacity: 1; /* Full opacity on hover/focus */ opacity: 1; /* Full opacity on hover/focus */
} }
} }
/** /**
* Type-specific styling * Type-specific styling
* Each notification type gets its own app icon color * Each notification type gets its own app icon color
@@ -220,72 +192,60 @@
color: var(--ios-success); /* Green for success */ color: var(--ios-success); /* Green for success */
} }
} }
&.fl-info { &.fl-info {
.fl-app-icon { .fl-app-icon {
color: var(--ios-info); /* Blue for info */ color: var(--ios-info); /* Blue for info */
} }
} }
&.fl-warning { &.fl-warning {
.fl-app-icon { .fl-app-icon {
color: var(--ios-warning); /* Orange for warning */ color: var(--ios-warning); /* Orange for warning */
} }
} }
&.fl-error { &.fl-error {
.fl-app-icon { .fl-app-icon {
color: var(--ios-error); /* Red for error */ color: var(--ios-error); /* Red for error */
} }
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-header { .fl-header {
padding-right: 0; padding-right: 0;
padding-left: 20px; /* Swap padding for RTL */ padding-left: 20px; /* Swap padding for RTL */
} }
.fl-app-icon { .fl-app-icon {
margin-right: 0; margin-right: 0;
margin-left: 8px; /* Swap margins for RTL */ margin-left: 8px; /* Swap margins for RTL */
} }
.fl-time { .fl-time {
margin-left: 0; margin-left: 0;
margin-right: 5px; /* Swap margins for RTL */ margin-right: 5px; /* Swap margins for RTL */
} }
.fl-message { .fl-message {
padding-right: 0; padding-right: 0;
padding-left: 15px; /* Swap padding for RTL */ padding-left: 15px; /* Swap padding for RTL */
} }
.fl-close { .fl-close {
right: auto; right: auto;
left: 12px; /* Move close button to left for RTL */ left: 12px; /* Move close button to left for RTL */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
*/ */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
animation: none; /* Disable entrance animation */ animation: none; /* Disable entrance animation */
.fl-content { .fl-content {
animation: none; /* Disable content expansion */ animation: none; /* Disable content expansion */
max-height: none; /* Show full content */ max-height: none; /* Show full content */
opacity: 1; /* Ensure content is visible */ opacity: 1; /* Ensure content is visible */
} }
} }
/** /**
* Mobile optimization * Mobile optimization
* Full width on small screens * Full width on small screens
@@ -294,8 +254,6 @@
width: 100%; /* Full width on mobile */ width: 100%; /* Full width on mobile */
} }
} }
/**
* Dark mode support * Dark mode support
* iOS-style dark appearance * iOS-style dark appearance
*/ */
@@ -307,11 +265,9 @@ html.fl-dark .fl-ios,
color: var(--ios-text-dark); /* Light text */ color: var(--ios-text-dark); /* Light text */
box-shadow: var(--ios-shadow-dark); /* Stronger shadow */ box-shadow: var(--ios-shadow-dark); /* Stronger shadow */
} }
.fl-time { .fl-time {
color: var(--ios-text-secondary-dark); /* Lighter secondary text */ color: var(--ios-text-secondary-dark); /* Lighter secondary text */
} }
.fl-close { .fl-close {
background-color: rgba(255, 255, 255, 0.2); /* Lighter background for close button */ background-color: rgba(255, 255, 255, 0.2); /* Lighter background for close button */
color: var(--ios-text-dark); /* Light text color */ color: var(--ios-text-dark); /* Light text color */
@@ -1,68 +1,19 @@
/**
* @file PHPFlasher iOS Theme Implementation
* @description Apple iOS-style notification interface
* @author Younes ENNAJI
*/
import './ios.scss' import './ios.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* iOS-inspired notification theme for PHPFlasher.
*
* This theme replicates Apple's iOS notification style with:
* - Frosted glass appearance with backdrop blur
* - App icon and name header
* - Current time display
* - Subtle animations and transitions
* - Close button in iOS style
* - Full dark mode support (resembles iOS dark mode)
*
* The theme aims to provide a native-like experience for users familiar with
* iOS devices, creating notifications that feel integrated with the platform.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { iosTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('ios', iosTheme);
*
* // Use the theme
* flasher.use('theme.ios').success('Your photo was uploaded successfully');
* ```
*/
export const iosTheme = { export const iosTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates an iOS-style notification with app icon, title/app name,
* timestamp, message content, and close button.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message, title } = envelope const { type, message, title } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
// Default app name (used if no title is provided)
const appName = 'PHPFlasher' const appName = 'PHPFlasher'
// Format current time in iOS style (hour:minute)
const now = new Date() const now = new Date()
const timeString = now.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }) const timeString = now.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
/**
* Gets the appropriate icon based on notification type.
* Each type has a specific icon matching iOS visual style.
*
* @returns SVG markup for the notification icon
*/
const getIcon = () => { const getIcon = () => {
switch (type) { switch (type) {
case 'success': case 'success':
@@ -85,7 +36,6 @@ export const iosTheme = {
return '' return ''
} }
// Use provided title or default to the app name
const displayTitle = title || appName const displayTitle = title || appName
return ` return `
@@ -1,29 +1,4 @@
/**
* @file PHPFlasher Jade Theme Registration
* @description Registers the Jade theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { jadeTheme } from './jade' import { jadeTheme } from './jade'
/**
* Register the Jade theme.
*
* This theme provides a calm, minimalist notification style with
* soft colors and refined animations.
*
* The registration makes the theme available under the name 'jade'.
*
* The Jade theme can be used by calling:
* ```typescript
* flasher.use('theme.jade').success('Your changes have been saved');
* ```
*
* Key features:
* - Clean, minimalist design with no icons
* - Soft color palette with pastel tones
* - Generous rounded corners for a friendly appearance
* - Subtle entrance animation with scale and fade effects
* - Type-specific background and text colors
*/
flasher.addTheme('jade', jadeTheme) flasher.addTheme('jade', jadeTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Jade Theme Styles
* @description CSS styling for minimalist Jade theme
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Jade Theme * PHPFlasher - Jade Theme
* *
* Calm, soothing notification style with elegant minimalism. * Calm, soothing notification style with elegant minimalism.
@@ -12,7 +6,6 @@
*/ */
.fl-jade { .fl-jade {
/* Theme variables - Define the visual appearance of Jade notifications */ /* Theme variables - Define the visual appearance of Jade notifications */
/* Base colors and appearance */ /* Base colors and appearance */
--jade-text-light: #5f6c7b; /* Soft slate gray for text */ --jade-text-light: #5f6c7b; /* Soft slate gray for text */
--jade-text-dark: #e2e8f0; /* Light gray for dark mode text */ --jade-text-dark: #e2e8f0; /* Light gray for dark mode text */
@@ -20,7 +13,6 @@
--jade-shadow-dark: 0 8px 24px rgba(0, 0, 0, 0.2); /* Stronger shadow for dark mode */ --jade-shadow-dark: 0 8px 24px rgba(0, 0, 0, 0.2); /* Stronger shadow for dark mode */
--jade-border-radius: 4px; /* Large rounded corners */ --jade-border-radius: 4px; /* Large rounded corners */
--jade-transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* Material-inspired transition */ --jade-transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* Material-inspired transition */
/* Type-specific colors and backgrounds - Light mode */ /* Type-specific colors and backgrounds - Light mode */
--jade-success-bg: #f0fdf4; /* Very light green background */ --jade-success-bg: #f0fdf4; /* Very light green background */
--jade-success-color: #16a34a; /* Green text */ --jade-success-color: #16a34a; /* Green text */
@@ -30,15 +22,12 @@
--jade-warning-color: #f59e0b; /* Orange text */ --jade-warning-color: #f59e0b; /* Orange text */
--jade-error-bg: #fef2f2; /* Very light red background */ --jade-error-bg: #fef2f2; /* Very light red background */
--jade-error-color: #dc2626; /* Red text */ --jade-error-color: #dc2626; /* Red text */
/* Dark mode backgrounds - Semi-transparent colored overlays */ /* Dark mode backgrounds - Semi-transparent colored overlays */
--jade-success-bg-dark: rgba(22, 163, 74, 0.15); /* Semi-transparent green */ --jade-success-bg-dark: rgba(22, 163, 74, 0.15); /* Semi-transparent green */
--jade-info-bg-dark: rgba(59, 130, 246, 0.15); /* Semi-transparent blue */ --jade-info-bg-dark: rgba(59, 130, 246, 0.15); /* Semi-transparent blue */
--jade-warning-bg-dark: rgba(245, 158, 11, 0.15); /* Semi-transparent orange */ --jade-warning-bg-dark: rgba(245, 158, 11, 0.15); /* Semi-transparent orange */
--jade-error-bg-dark: rgba(220, 38, 38, 0.15); /* Semi-transparent red */ --jade-error-bg-dark: rgba(220, 38, 38, 0.15); /* Semi-transparent red */
} }
/**
* Entrance animation for Jade theme * Entrance animation for Jade theme
* Combines subtle upward movement with scaling for a refined appearance * Combines subtle upward movement with scaling for a refined appearance
*/ */
@@ -52,8 +41,6 @@
transform: translateY(0) scale(1); /* End at normal size and position */ transform: translateY(0) scale(1); /* End at normal size and position */
} }
} }
/**
* Base Jade theme styling * Base Jade theme styling
*/ */
.fl-jade { .fl-jade {
@@ -67,7 +54,6 @@
will-change: transform, opacity; /* Optimize for animation performance */ will-change: transform, opacity; /* Optimize for animation performance */
border: 1px solid transparent; /* Border for type-specific colors */ border: 1px solid transparent; /* Border for type-specific colors */
overflow: hidden; /* Contain progress bar */ overflow: hidden; /* Contain progress bar */
/** /**
* Content container * Content container
* Holds message and close button * Holds message and close button
@@ -76,7 +62,6 @@
display: flex; display: flex;
align-items: center; /* Vertically center content */ align-items: center; /* Vertically center content */
} }
/** /**
* Message styling * Message styling
* Main notification text * Main notification text
@@ -88,7 +73,6 @@
font-weight: 500; /* Medium weight for better readability */ font-weight: 500; /* Medium weight for better readability */
padding-right: 0.75rem; /* Space between text and close button */ padding-right: 0.75rem; /* Space between text and close button */
} }
/** /**
* Close button styling * Close button styling
* Circular button with hover effect * Circular button with hover effect
@@ -108,13 +92,11 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-shrink: 0; /* Prevent button from shrinking */ flex-shrink: 0; /* Prevent button from shrinking */
&:hover, &:focus { &:hover, &:focus {
opacity: 1; /* Full opacity on hover/focus */ opacity: 1; /* Full opacity on hover/focus */
background-color: rgba(0, 0, 0, 0.05); /* Subtle background on hover */ background-color: rgba(0, 0, 0, 0.05); /* Subtle background on hover */
} }
} }
/** /**
* Progress bar container * Progress bar container
* Holds the animated progress indicator * Holds the animated progress indicator
@@ -128,7 +110,6 @@
overflow: hidden; overflow: hidden;
border-radius: 0 0 var(--jade-border-radius) var(--jade-border-radius); /* Match parent corners */ border-radius: 0 0 var(--jade-border-radius) var(--jade-border-radius); /* Match parent corners */
opacity: 0.7; /* Slightly transparent */ opacity: 0.7; /* Slightly transparent */
/** /**
* Progress indicator * Progress indicator
* Animated by JavaScript to show remaining time * Animated by JavaScript to show remaining time
@@ -138,9 +119,7 @@
width: 100%; width: 100%;
} }
} }
/* Type-specific styling for each notification type */ /* Type-specific styling for each notification type */
/** /**
* Success notification styling * Success notification styling
* Green theme with appropriate hover states * Green theme with appropriate hover states
@@ -149,20 +128,16 @@
background-color: var(--jade-success-bg); background-color: var(--jade-success-bg);
color: var(--jade-success-color); color: var(--jade-success-color);
border-color: rgba(22, 163, 74, 0.1); /* Very subtle border */ border-color: rgba(22, 163, 74, 0.1); /* Very subtle border */
.fl-close { .fl-close {
color: var(--jade-success-color); /* Green close button */ color: var(--jade-success-color); /* Green close button */
&:hover, &:focus { &:hover, &:focus {
background-color: rgba(22, 163, 74, 0.1); /* Green hover state */ background-color: rgba(22, 163, 74, 0.1); /* Green hover state */
} }
} }
.fl-progress-bar .fl-progress { .fl-progress-bar .fl-progress {
background-color: var(--jade-success-color); /* Green progress bar */ background-color: var(--jade-success-color); /* Green progress bar */
} }
} }
/** /**
* Info notification styling * Info notification styling
* Blue theme with appropriate hover states * Blue theme with appropriate hover states
@@ -171,20 +146,16 @@
background-color: var(--jade-info-bg); background-color: var(--jade-info-bg);
color: var(--jade-info-color); color: var(--jade-info-color);
border-color: rgba(59, 130, 246, 0.1); /* Very subtle border */ border-color: rgba(59, 130, 246, 0.1); /* Very subtle border */
.fl-close { .fl-close {
color: var(--jade-info-color); /* Blue close button */ color: var(--jade-info-color); /* Blue close button */
&:hover, &:focus { &:hover, &:focus {
background-color: rgba(59, 130, 246, 0.1); /* Blue hover state */ background-color: rgba(59, 130, 246, 0.1); /* Blue hover state */
} }
} }
.fl-progress-bar .fl-progress { .fl-progress-bar .fl-progress {
background-color: var(--jade-info-color); /* Blue progress bar */ background-color: var(--jade-info-color); /* Blue progress bar */
} }
} }
/** /**
* Warning notification styling * Warning notification styling
* Orange theme with appropriate hover states * Orange theme with appropriate hover states
@@ -193,20 +164,16 @@
background-color: var(--jade-warning-bg); background-color: var(--jade-warning-bg);
color: var(--jade-warning-color); color: var(--jade-warning-color);
border-color: rgba(245, 158, 11, 0.1); /* Very subtle border */ border-color: rgba(245, 158, 11, 0.1); /* Very subtle border */
.fl-close { .fl-close {
color: var(--jade-warning-color); /* Orange close button */ color: var(--jade-warning-color); /* Orange close button */
&:hover, &:focus { &:hover, &:focus {
background-color: rgba(245, 158, 11, 0.1); /* Orange hover state */ background-color: rgba(245, 158, 11, 0.1); /* Orange hover state */
} }
} }
.fl-progress-bar .fl-progress { .fl-progress-bar .fl-progress {
background-color: var(--jade-warning-color); /* Orange progress bar */ background-color: var(--jade-warning-color); /* Orange progress bar */
} }
} }
/** /**
* Error notification styling * Error notification styling
* Red theme with appropriate hover states * Red theme with appropriate hover states
@@ -215,37 +182,30 @@
background-color: var(--jade-error-bg); background-color: var(--jade-error-bg);
color: var(--jade-error-color); color: var(--jade-error-color);
border-color: rgba(220, 38, 38, 0.1); /* Very subtle border */ border-color: rgba(220, 38, 38, 0.1); /* Very subtle border */
.fl-close { .fl-close {
color: var(--jade-error-color); /* Red close button */ color: var(--jade-error-color); /* Red close button */
&:hover, &:focus { &:hover, &:focus {
background-color: rgba(220, 38, 38, 0.1); /* Red hover state */ background-color: rgba(220, 38, 38, 0.1); /* Red hover state */
} }
} }
.fl-progress-bar .fl-progress { .fl-progress-bar .fl-progress {
background-color: var(--jade-error-color); /* Red progress bar */ background-color: var(--jade-error-color); /* Red progress bar */
} }
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-message { .fl-message {
padding-right: 0; padding-right: 0;
padding-left: 0.75rem; /* Swap padding for RTL */ padding-left: 0.75rem; /* Swap padding for RTL */
} }
.fl-progress .fl-progress { .fl-progress .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
@@ -254,8 +214,6 @@
animation: none; /* Disable animation */ animation: none; /* Disable animation */
} }
} }
/**
* Dark mode support * Dark mode support
* Different styling when in dark mode * Different styling when in dark mode
*/ */
@@ -264,7 +222,6 @@ html.fl-dark .fl-jade,
.fl-jade.fl-auto-dark { .fl-jade.fl-auto-dark {
box-shadow: var(--jade-shadow-dark); /* Stronger shadow in dark mode */ box-shadow: var(--jade-shadow-dark); /* Stronger shadow in dark mode */
color: var(--jade-text-dark); /* Lighter text */ color: var(--jade-text-dark); /* Lighter text */
/** /**
* Type-specific backgrounds for dark mode * Type-specific backgrounds for dark mode
* Uses semi-transparent colored overlays * Uses semi-transparent colored overlays
@@ -273,22 +230,18 @@ html.fl-dark .fl-jade,
background-color: var(--jade-success-bg-dark); background-color: var(--jade-success-bg-dark);
border-color: rgba(22, 163, 74, 0.2); /* Slightly stronger border */ border-color: rgba(22, 163, 74, 0.2); /* Slightly stronger border */
} }
&.fl-info { &.fl-info {
background-color: var(--jade-info-bg-dark); background-color: var(--jade-info-bg-dark);
border-color: rgba(59, 130, 246, 0.2); /* Slightly stronger border */ border-color: rgba(59, 130, 246, 0.2); /* Slightly stronger border */
} }
&.fl-warning { &.fl-warning {
background-color: var(--jade-warning-bg-dark); background-color: var(--jade-warning-bg-dark);
border-color: rgba(245, 158, 11, 0.2); /* Slightly stronger border */ border-color: rgba(245, 158, 11, 0.2); /* Slightly stronger border */
} }
&.fl-error { &.fl-error {
background-color: var(--jade-error-bg-dark); background-color: var(--jade-error-bg-dark);
border-color: rgba(220, 38, 38, 0.2); /* Slightly stronger border */ border-color: rgba(220, 38, 38, 0.2); /* Slightly stronger border */
} }
/** /**
* Dark mode hover states * Dark mode hover states
* Lighter background for better visibility * Lighter background for better visibility
@@ -1,56 +1,10 @@
/**
* @file PHPFlasher Jade Theme Implementation
* @description Minimalist notification theme with soft, natural aesthetics
* @author Younes ENNAJI
*/
import './jade.scss' import './jade.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Jade notification theme for PHPFlasher.
*
* The Jade theme provides a clean, minimal aesthetic with:
* - Soft, pastel color palette inspired by natural tones
* - Generous border radius for a friendly appearance
* - Subtle entrance animation with scale and fade
* - Type-specific colored backgrounds and text
* - Minimalist layout without icons for a clean look
* - Refined hover interactions and transitions
*
* This theme prioritizes readability and calmness, making it
* ideal for applications where subtle notifications are preferred.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { jadeTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('jade', jadeTheme);
*
* // Use the theme
* flasher.use('theme.jade').success('Your changes have been saved');
* ```
*/
export const jadeTheme = { export const jadeTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a clean, minimal notification with just the essential elements:
* - Message text
* - Close button
* - Progress indicator
*
* The Jade theme deliberately omits icons and titles for a more streamlined
* appearance, focusing purely on the message content.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,28 +1,4 @@
/**
* @file PHPFlasher Material Design Theme Registration
* @description Registers the Material Design theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { materialTheme } from './material' import { materialTheme } from './material'
/**
* Register the Material Design theme.
*
* This theme provides minimalist notifications styled after Google's Material Design system.
* The registration makes the theme available under the name 'material'.
*
* The Material theme can be used by calling:
* ```typescript
* flasher.use('theme.material').success('Operation completed successfully');
* ```
*
* Key features:
* - Clean Material Design card with proper elevation
* - Material typography system with Roboto font
* - UPPERCASE action button text following Material guidelines
* - Ripple effect on button interaction
* - Linear progress indicator
* - Minimalist design without icons
*/
flasher.addTheme('material', materialTheme) flasher.addTheme('material', materialTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Material Design Theme Styles
* @description CSS styling for minimalist Material Design notifications
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Material Design Theme * PHPFlasher - Material Design Theme
* *
* Google's Material Design-inspired notifications with a minimalist approach. * Google's Material Design-inspired notifications with a minimalist approach.
@@ -12,7 +6,6 @@
*/ */
.fl-material { .fl-material {
/* Theme variables - Define the visual appearance of Material Design notifications */ /* Theme variables - Define the visual appearance of Material Design notifications */
/* Base colors and appearance for light and dark modes */ /* Base colors and appearance for light and dark modes */
--md-bg-light: #ffffff; /* Card background in light mode */ --md-bg-light: #ffffff; /* Card background in light mode */
--md-bg-dark: #2d2d2d; /* Card background in dark mode */ --md-bg-dark: #2d2d2d; /* Card background in dark mode */
@@ -20,29 +13,23 @@
--md-text-secondary-light: rgba(0, 0, 0, 0.6); /* Secondary text in light mode (60% black) */ --md-text-secondary-light: rgba(0, 0, 0, 0.6); /* Secondary text in light mode (60% black) */
--md-text-dark: rgba(255, 255, 255, 0.87); /* Primary text in dark mode (87% white) */ --md-text-dark: rgba(255, 255, 255, 0.87); /* Primary text in dark mode (87% white) */
--md-text-secondary-dark: rgba(255, 255, 255, 0.6); /* Secondary text in dark mode (60% white) */ --md-text-secondary-dark: rgba(255, 255, 255, 0.6); /* Secondary text in dark mode (60% white) */
/* Material Design elevation - multi-layered shadows */ /* Material Design elevation - multi-layered shadows */
--md-elevation: 0 3px 5px -1px rgba(0,0,0,0.2), /* Ambient shadow */ --md-elevation: 0 3px 5px -1px rgba(0,0,0,0.2), /* Ambient shadow */
0 6px 10px 0 rgba(0,0,0,0.14), /* Penumbra shadow */ 0 6px 10px 0 rgba(0,0,0,0.14), /* Penumbra shadow */
0 1px 18px 0 rgba(0,0,0,0.12); /* Umbra shadow */ 0 1px 18px 0 rgba(0,0,0,0.12); /* Umbra shadow */
--md-elevation-dark: 0 3px 5px -1px rgba(0,0,0,0.4), /* Darker ambient shadow */ --md-elevation-dark: 0 3px 5px -1px rgba(0,0,0,0.4), /* Darker ambient shadow */
0 6px 10px 0 rgba(0,0,0,0.28), /* Darker penumbra shadow */ 0 6px 10px 0 rgba(0,0,0,0.28), /* Darker penumbra shadow */
0 1px 18px 0 rgba(0,0,0,0.24); /* Darker umbra shadow */ 0 1px 18px 0 rgba(0,0,0,0.24); /* Darker umbra shadow */
--md-border-radius: 4px; /* Material Design default corner radius */ --md-border-radius: 4px; /* Material Design default corner radius */
/* Material Design color palette for notification types */ /* Material Design color palette for notification types */
--md-success: #43a047; /* Green 600 from Material palette */ --md-success: #43a047; /* Green 600 from Material palette */
--md-info: #1e88e5; /* Blue 600 from Material palette */ --md-info: #1e88e5; /* Blue 600 from Material palette */
--md-warning: #fb8c00; /* Orange 600 from Material palette */ --md-warning: #fb8c00; /* Orange 600 from Material palette */
--md-error: #e53935; /* Red 600 from Material palette */ --md-error: #e53935; /* Red 600 from Material palette */
/* Animation timing variables */ /* Animation timing variables */
--md-animation-duration: 0.3s; /* Standard Material motion duration */ --md-animation-duration: 0.3s; /* Standard Material motion duration */
--md-ripple-duration: 0.6s; /* Ripple effect duration */ --md-ripple-duration: 0.6s; /* Ripple effect duration */
} }
/**
* Entrance animation for Material theme * Entrance animation for Material theme
* Material Design-style slide up with easing * Material Design-style slide up with easing
*/ */
@@ -56,8 +43,6 @@
transform: translateY(0); /* End at final position */ transform: translateY(0); /* End at final position */
} }
} }
/**
* Material Design ripple effect animation * Material Design ripple effect animation
* Creates expanding circle from point of interaction * Creates expanding circle from point of interaction
*/ */
@@ -67,8 +52,6 @@
opacity: 0; /* Fade out completely */ opacity: 0; /* Fade out completely */
} }
} }
/**
* Base Material Design theme styling * Base Material Design theme styling
*/ */
.fl-material { .fl-material {
@@ -76,7 +59,6 @@
margin: 8px 0; /* Spacing between notifications */ margin: 8px 0; /* Spacing between notifications */
font-family: Roboto, "Segoe UI", Helvetica, Arial, sans-serif; /* Material uses Roboto */ font-family: Roboto, "Segoe UI", Helvetica, Arial, sans-serif; /* Material uses Roboto */
animation: mdSlideUp var(--md-animation-duration) cubic-bezier(0.4, 0, 0.2, 1); /* Material standard easing */ animation: mdSlideUp var(--md-animation-duration) cubic-bezier(0.4, 0, 0.2, 1); /* Material standard easing */
/** /**
* Main card container * Main card container
* Follows Material Design card component styling * Follows Material Design card component styling
@@ -88,7 +70,6 @@
box-shadow: var(--md-elevation); /* Multi-layered shadow for depth */ box-shadow: var(--md-elevation); /* Multi-layered shadow for depth */
overflow: hidden; /* Contains progress bar */ overflow: hidden; /* Contains progress bar */
} }
/** /**
* Content section * Content section
* Contains message text * Contains message text
@@ -98,7 +79,6 @@
display: flex; display: flex;
align-items: flex-start; /* Align items to top */ align-items: flex-start; /* Align items to top */
} }
/** /**
* Text content container * Text content container
* Holds message * Holds message
@@ -106,7 +86,6 @@
.fl-text-content { .fl-text-content {
flex: 1; /* Take available space */ flex: 1; /* Take available space */
} }
/** /**
* Message styling * Message styling
* Following Material Design typography * Following Material Design typography
@@ -116,7 +95,6 @@
line-height: 1.43; /* Material line height for body 2 */ line-height: 1.43; /* Material line height for body 2 */
color: var(--md-text-secondary-light); /* Secondary text color (60% opacity) */ color: var(--md-text-secondary-light); /* Secondary text color (60% opacity) */
} }
/** /**
* Action buttons section * Action buttons section
* Contains dismiss button * Contains dismiss button
@@ -126,7 +104,6 @@
justify-content: flex-end; /* Align buttons to right */ justify-content: flex-end; /* Align buttons to right */
padding: 8px; /* Standard Material padding */ padding: 8px; /* Standard Material padding */
} }
/** /**
* Action button styling * Action button styling
* Following Material Design "text button" guidelines * Following Material Design "text button" guidelines
@@ -146,11 +123,9 @@
transition: background-color 0.2s; /* Smooth hover effect */ transition: background-color 0.2s; /* Smooth hover effect */
position: relative; /* For ripple positioning */ position: relative; /* For ripple positioning */
overflow: hidden; /* Contain ripple effect */ overflow: hidden; /* Contain ripple effect */
&:hover, &:focus { &:hover, &:focus {
background-color: rgba(0, 0, 0, 0.04); /* Material hover state - 4% opacity */ background-color: rgba(0, 0, 0, 0.04); /* Material hover state - 4% opacity */
} }
/** /**
* Ripple effect * Ripple effect
* Material Design's signature ink ripple on interaction * Material Design's signature ink ripple on interaction
@@ -166,13 +141,11 @@
transform: scale(1); transform: scale(1);
pointer-events: none; /* Don't interfere with clicks */ pointer-events: none; /* Don't interfere with clicks */
} }
&:active::after { &:active::after {
opacity: 0.3; /* Material ripple opacity */ opacity: 0.3; /* Material ripple opacity */
animation: mdRipple var(--md-ripple-duration) linear; /* Expand and fade */ animation: mdRipple var(--md-ripple-duration) linear; /* Expand and fade */
} }
} }
/** /**
* Type-specific styling * Type-specific styling
* Each notification type has its own color based on Material palette * Each notification type has its own color based on Material palette
@@ -181,42 +154,34 @@
.fl-icon-wrapper { .fl-icon-wrapper {
color: var(--md-success); /* Green icon */ color: var(--md-success); /* Green icon */
} }
.fl-action-button { .fl-action-button {
color: var(--md-success); /* Green button */ color: var(--md-success); /* Green button */
} }
} }
&.fl-info { &.fl-info {
.fl-icon-wrapper { .fl-icon-wrapper {
color: var(--md-info); /* Blue icon */ color: var(--md-info); /* Blue icon */
} }
.fl-action-button { .fl-action-button {
color: var(--md-info); /* Blue button */ color: var(--md-info); /* Blue button */
} }
} }
&.fl-warning { &.fl-warning {
.fl-icon-wrapper { .fl-icon-wrapper {
color: var(--md-warning); /* Orange icon */ color: var(--md-warning); /* Orange icon */
} }
.fl-action-button { .fl-action-button {
color: var(--md-warning); /* Orange button */ color: var(--md-warning); /* Orange button */
} }
} }
&.fl-error { &.fl-error {
.fl-icon-wrapper { .fl-icon-wrapper {
color: var(--md-error); /* Red icon */ color: var(--md-error); /* Red icon */
} }
.fl-action-button { .fl-action-button {
color: var(--md-error); /* Red button */ color: var(--md-error); /* Red button */
} }
} }
/** /**
* Progress bar * Progress bar
* Material Design linear progress indicator * Material Design linear progress indicator
@@ -228,14 +193,12 @@
right: 0; right: 0;
height: 4px; /* Material's standard progress height */ height: 4px; /* Material's standard progress height */
overflow: hidden; overflow: hidden;
.fl-progress { .fl-progress {
height: 100%; height: 100%;
width: 100%; width: 100%;
transform-origin: left center; /* Animation starts from left */ transform-origin: left center; /* Animation starts from left */
} }
} }
/** /**
* Type-specific progress colors * Type-specific progress colors
* Each notification type has its own progress bar color * Each notification type has its own progress bar color
@@ -243,58 +206,46 @@
&.fl-success .fl-progress { &.fl-success .fl-progress {
background-color: var(--md-success); /* Green progress */ background-color: var(--md-success); /* Green progress */
} }
&.fl-info .fl-progress { &.fl-info .fl-progress {
background-color: var(--md-info); /* Blue progress */ background-color: var(--md-info); /* Blue progress */
} }
&.fl-warning .fl-progress { &.fl-warning .fl-progress {
background-color: var(--md-warning); /* Orange progress */ background-color: var(--md-warning); /* Orange progress */
} }
&.fl-error .fl-progress { &.fl-error .fl-progress {
background-color: var(--md-error); /* Red progress */ background-color: var(--md-error); /* Red progress */
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-content { .fl-content {
flex-direction: row-reverse; /* Reverse flex direction */ flex-direction: row-reverse; /* Reverse flex direction */
} }
.fl-icon-wrapper { .fl-icon-wrapper {
margin-right: 0; margin-right: 0;
margin-left: 16px; /* Swap margins for RTL */ margin-left: 16px; /* Swap margins for RTL */
} }
.fl-actions { .fl-actions {
justify-content: flex-start; /* Align buttons to left in RTL */ justify-content: flex-start; /* Align buttons to left in RTL */
} }
.fl-progress { .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
*/ */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
animation: none; /* Disable slide animation */ animation: none; /* Disable slide animation */
.fl-action-button:active::after { .fl-action-button:active::after {
animation: none; /* Disable ripple animation */ animation: none; /* Disable ripple animation */
} }
} }
} }
/**
* Dark mode support * Dark mode support
* Material Design dark theme implementation * Material Design dark theme implementation
*/ */
@@ -306,11 +257,9 @@ html.fl-dark .fl-material,
color: var(--md-text-dark); /* Lighter text */ color: var(--md-text-dark); /* Lighter text */
box-shadow: var(--md-elevation-dark); /* Stronger shadows */ box-shadow: var(--md-elevation-dark); /* Stronger shadows */
} }
.fl-message { .fl-message {
color: var(--md-text-secondary-dark); /* Lighter secondary text */ color: var(--md-text-secondary-dark); /* Lighter secondary text */
} }
.fl-action-button { .fl-action-button {
&:hover, &:focus { &:hover, &:focus {
background-color: rgba(255, 255, 255, 0.08); /* Material dark hover - 8% opacity */ background-color: rgba(255, 255, 255, 0.08); /* Material dark hover - 8% opacity */
@@ -1,57 +1,14 @@
/**
* @file PHPFlasher Material Design Theme Implementation
* @description Minimalist Material Design notification theme
* @author Younes ENNAJI
*/
import './material.scss' import './material.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Material Design notification theme for PHPFlasher.
*
* This theme implements Google's Material Design principles with a minimalist approach:
* - Clean card design with proper elevation shadow
* - Material Design typography with Roboto font
* - UPPERCASE action button following Material guidelines
* - Material "ink ripple" effect on button press
* - Material motion patterns for animations
* - Linear progress indicator
*
* Unlike the more comprehensive Google theme, this Material theme
* provides a simpler, more streamlined appearance without icons,
* focusing purely on the message content.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { materialTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('material', materialTheme);
*
* // Use the theme
* flasher.use('theme.material').success('Operation completed successfully');
* ```
*/
export const materialTheme = { export const materialTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a minimalist Material Design notification card with
* message content, dismiss button, and progress indicator.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
// Material Design uses uppercase text for buttons
const actionText = 'DISMISS' const actionText = 'DISMISS'
return ` return `
@@ -1,29 +1,4 @@
/**
* @file PHPFlasher Minimal Theme Registration
* @description Registers the Minimal theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { minimalTheme } from './minimal' import { minimalTheme } from './minimal'
/**
* Register the Minimal theme.
*
* This theme provides an ultra-clean, distraction-free notification style
* that integrates smoothly into modern interfaces without drawing unnecessary attention.
*
* The registration makes the theme available under the name 'minimal'.
*
* The Minimal theme can be used by calling:
* ```typescript
* flasher.use('theme.minimal').success('Changes saved');
* ```
*
* Key features:
* - Translucent background with subtle blur
* - Compact design with minimal visual elements
* - Small colored dot instead of large icons
* - Thin progress indicator
* - Short, subtle entrance animation
*/
flasher.addTheme('minimal', minimalTheme) flasher.addTheme('minimal', minimalTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Minimal Theme Styles
* @description CSS styling for ultra-clean, distraction-free notifications
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Minimal Theme * PHPFlasher - Minimal Theme
* *
* Ultra-clean and minimal notifications that stay out of the way. * Ultra-clean and minimal notifications that stay out of the way.
@@ -12,7 +6,6 @@
*/ */
.fl-minimal { .fl-minimal {
/* Theme variables - Define the visual appearance of Minimal notifications */ /* Theme variables - Define the visual appearance of Minimal notifications */
/* Base colors and appearance */ /* Base colors and appearance */
--minimal-bg-light: rgba(255, 255, 255); /* Semi-transparent white in light mode */ --minimal-bg-light: rgba(255, 255, 255); /* Semi-transparent white in light mode */
--minimal-bg-dark: rgba(25, 25, 25); /* Semi-transparent dark in dark mode */ --minimal-bg-dark: rgba(25, 25, 25); /* Semi-transparent dark in dark mode */
@@ -23,15 +16,12 @@
--minimal-border-radius: 4px; /* Modest border radius */ --minimal-border-radius: 4px; /* Modest border radius */
--minimal-border-color: rgba(0, 0, 0, 0.05); /* Nearly invisible border in light mode */ --minimal-border-color: rgba(0, 0, 0, 0.05); /* Nearly invisible border in light mode */
--minimal-border-color-dark: rgba(255, 255, 255, 0.1); /* Subtle border in dark mode */ --minimal-border-color-dark: rgba(255, 255, 255, 0.1); /* Subtle border in dark mode */
/* Type-specific colors - semi-transparent for subtlety */ /* Type-specific colors - semi-transparent for subtlety */
--minimal-success: rgba(34, 197, 94, 0.9); /* Green with slight transparency */ --minimal-success: rgba(34, 197, 94, 0.9); /* Green with slight transparency */
--minimal-info: rgba(14, 165, 233, 0.9); /* Blue with slight transparency */ --minimal-info: rgba(14, 165, 233, 0.9); /* Blue with slight transparency */
--minimal-warning: rgba(245, 158, 11, 0.9); /* Orange with slight transparency */ --minimal-warning: rgba(245, 158, 11, 0.9); /* Orange with slight transparency */
--minimal-error: rgba(239, 68, 68, 0.9); /* Red with slight transparency */ --minimal-error: rgba(239, 68, 68, 0.9); /* Red with slight transparency */
} }
/**
* Entrance animation for Minimal theme * Entrance animation for Minimal theme
* Quick, subtle slide-in from above * Quick, subtle slide-in from above
*/ */
@@ -45,8 +35,6 @@
transform: translateY(0); /* End at natural position */ transform: translateY(0); /* End at natural position */
} }
} }
/**
* Base Minimal theme styling * Base Minimal theme styling
*/ */
.fl-minimal { .fl-minimal {
@@ -63,7 +51,6 @@
backdrop-filter: blur(8px); /* Frosted glass effect */ backdrop-filter: blur(8px); /* Frosted glass effect */
-webkit-backdrop-filter: blur(8px); /* Safari support */ -webkit-backdrop-filter: blur(8px); /* Safari support */
border: 1px solid var(--minimal-border-color); /* Nearly invisible border */ border: 1px solid var(--minimal-border-color); /* Nearly invisible border */
/** /**
* Content container * Content container
* Holds message and close button * Holds message and close button
@@ -73,7 +60,6 @@
align-items: center; /* Vertically center content */ align-items: center; /* Vertically center content */
gap: 0.75rem; /* Space between elements */ gap: 0.75rem; /* Space between elements */
} }
/** /**
* Small colored dot indicator * Small colored dot indicator
* Used instead of large icons for minimal appearance * Used instead of large icons for minimal appearance
@@ -84,7 +70,6 @@
border-radius: 50%; /* Perfectly circular */ border-radius: 50%; /* Perfectly circular */
flex-shrink: 0; /* Prevent dot from shrinking */ flex-shrink: 0; /* Prevent dot from shrinking */
} }
/** /**
* Message styling * Message styling
* Main notification text * Main notification text
@@ -96,7 +81,6 @@
flex: 1; /* Take available space */ flex: 1; /* Take available space */
margin: 0; margin: 0;
} }
/** /**
* Close button styling * Close button styling
* Simple, minimal close button * Simple, minimal close button
@@ -116,12 +100,10 @@
height: 1.5rem; height: 1.5rem;
font-size: 1rem; /* 16px × symbol */ font-size: 1rem; /* 16px × symbol */
flex-shrink: 0; /* Prevent button from shrinking */ flex-shrink: 0; /* Prevent button from shrinking */
&:hover, &:focus { &:hover, &:focus {
opacity: 0.8; /* More visible on hover/focus, but still subtle */ opacity: 0.8; /* More visible on hover/focus, but still subtle */
} }
} }
/** /**
* Progress bar container * Progress bar container
* Holds the animated progress indicator * Holds the animated progress indicator
@@ -135,7 +117,6 @@
overflow: hidden; overflow: hidden;
border-radius: 0 0 var(--minimal-border-radius) var(--minimal-border-radius); /* Match parent corners */ border-radius: 0 0 var(--minimal-border-radius) var(--minimal-border-radius); /* Match parent corners */
opacity: 0.7; /* Slightly transparent */ opacity: 0.7; /* Slightly transparent */
/** /**
* Progress indicator * Progress indicator
* Animated by JavaScript to show remaining time * Animated by JavaScript to show remaining time
@@ -145,7 +126,6 @@
width: 100%; width: 100%;
} }
} }
/** /**
* Type-specific styling * Type-specific styling
* Each notification type gets its own color for the dot and progress bar * Each notification type gets its own color for the dot and progress bar
@@ -154,34 +134,28 @@
.fl-dot { background-color: var(--minimal-success); } .fl-dot { background-color: var(--minimal-success); }
.fl-progress-bar .fl-progress { background-color: var(--minimal-success); } .fl-progress-bar .fl-progress { background-color: var(--minimal-success); }
} }
&.fl-info { &.fl-info {
.fl-dot { background-color: var(--minimal-info); } .fl-dot { background-color: var(--minimal-info); }
.fl-progress-bar .fl-progress { background-color: var(--minimal-info); } .fl-progress-bar .fl-progress { background-color: var(--minimal-info); }
} }
&.fl-warning { &.fl-warning {
.fl-dot { background-color: var(--minimal-warning); } .fl-dot { background-color: var(--minimal-warning); }
.fl-progress-bar .fl-progress { background-color: var(--minimal-warning); } .fl-progress-bar .fl-progress { background-color: var(--minimal-warning); }
} }
&.fl-error { &.fl-error {
.fl-dot { background-color: var(--minimal-error); } .fl-dot { background-color: var(--minimal-error); }
.fl-progress-bar .fl-progress { background-color: var(--minimal-error); } .fl-progress-bar .fl-progress { background-color: var(--minimal-error); }
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-progress .fl-progress { .fl-progress .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
@@ -190,8 +164,6 @@
animation: none; /* Disable animation */ animation: none; /* Disable animation */
} }
} }
/**
* Dark mode support * Dark mode support
* Different styling when in dark mode * Different styling when in dark mode
*/ */
@@ -1,57 +1,10 @@
/**
* @file PHPFlasher Minimal Theme Implementation
* @description Ultra-clean notification design that stays out of the way
* @author Younes ENNAJI
*/
import './minimal.scss' import './minimal.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Minimal notification theme for PHPFlasher.
*
* The Minimal theme provides an ultra-clean, distraction-free design with:
* - Translucent background with subtle blur effect
* - Small dot indicator instead of large icons
* - Compact dimensions and comfortable spacing
* - Subtle entrance animation
* - Thin progress indicator
* - System font stack for maximum readability
*
* This theme is designed to be as unobtrusive as possible, perfect for
* applications where notifications should provide information without
* disrupting the user experience.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { minimalTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('minimal', minimalTheme);
*
* // Use the theme
* flasher.use('theme.minimal').success('Changes saved');
* ```
*/
export const minimalTheme = { export const minimalTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a clean, minimal notification with just the essential elements:
* - Message text
* - Close button
* - Progress indicator
*
* The Minimal theme uses a color dot indicator instead of large icons,
* keeping the design compact and focused on the message content.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,29 +1,4 @@
/**
* @file PHPFlasher Neon Theme Registration
* @description Registers the Neon theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { neonTheme } from './neon' import { neonTheme } from './neon'
/**
* Register the Neon theme.
*
* This theme provides elegant notifications with subtle glowing accents
* and a sophisticated visual style.
*
* The registration makes the theme available under the name 'neon'.
*
* The Neon theme can be used by calling:
* ```typescript
* flasher.use('theme.neon').success('Your changes have been saved');
* ```
*
* Key features:
* - Subtle glowing accents based on notification type
* - Floating illuminated circular indicator
* - Frosted glass background with blur effect
* - Refined typography with Inter font
* - Smooth entrance animation with blur transition
*/
flasher.addTheme('neon', neonTheme) flasher.addTheme('neon', neonTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Neon Theme Styles
* @description CSS styling for elegant notifications with glowing accents
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Neon Theme * PHPFlasher - Neon Theme
* *
* Notifications with subtle glowing accents and elegant typography. * Notifications with subtle glowing accents and elegant typography.
@@ -12,7 +6,6 @@
*/ */
.fl-neon { .fl-neon {
/* Theme variables - Define the visual appearance of Neon notifications */ /* Theme variables - Define the visual appearance of Neon notifications */
/* Base colors and appearance */ /* Base colors and appearance */
--neon-bg-light: rgba(255, 255, 255); /* Translucent white in light mode */ --neon-bg-light: rgba(255, 255, 255); /* Translucent white in light mode */
--neon-bg-dark: rgba(15, 23, 42); /* Translucent slate in dark mode */ --neon-bg-dark: rgba(15, 23, 42); /* Translucent slate in dark mode */
@@ -21,19 +14,15 @@
--neon-shadow: 0 8px 30px rgba(0, 0, 0, 0.12); /* Soft, spread shadow */ --neon-shadow: 0 8px 30px rgba(0, 0, 0, 0.12); /* Soft, spread shadow */
--neon-shadow-dark: 0 8px 30px rgba(0, 0, 0, 0.25); /* Deeper shadow for dark mode */ --neon-shadow-dark: 0 8px 30px rgba(0, 0, 0, 0.25); /* Deeper shadow for dark mode */
--neon-border-radius: 4px; /* Rounded corners */ --neon-border-radius: 4px; /* Rounded corners */
/* Glow colors for different notification types */ /* Glow colors for different notification types */
--neon-success: #10b981; /* Emerald green for success */ --neon-success: #10b981; /* Emerald green for success */
--neon-info: #3b82f6; /* Blue for info */ --neon-info: #3b82f6; /* Blue for info */
--neon-warning: #f59e0b; /* Amber for warning */ --neon-warning: #f59e0b; /* Amber for warning */
--neon-error: #ef4444; /* Red for error */ --neon-error: #ef4444; /* Red for error */
/* Glow and animation properties */ /* Glow and animation properties */
--neon-glow-strength: 10px; /* How far the glow extends */ --neon-glow-strength: 10px; /* How far the glow extends */
--neon-animation-duration: 0.35s; /* Duration for entrance animation */ --neon-animation-duration: 0.35s; /* Duration for entrance animation */
} }
/**
* Entrance animation for Neon theme * Entrance animation for Neon theme
* Combines movement, fade, and blur transitions * Combines movement, fade, and blur transitions
*/ */
@@ -49,8 +38,6 @@
filter: blur(0); /* End with clear focus */ filter: blur(0); /* End with clear focus */
} }
} }
/**
* Pulsing glow animation for the indicator * Pulsing glow animation for the indicator
* Creates a gentle breathing effect for the glow * Creates a gentle breathing effect for the glow
*/ */
@@ -62,8 +49,6 @@
filter: drop-shadow(0 0 calc(var(--neon-glow-strength) * 0.7) currentColor); /* Reduced glow */ filter: drop-shadow(0 0 calc(var(--neon-glow-strength) * 0.7) currentColor); /* Reduced glow */
} }
} }
/**
* Base Neon theme styling * Base Neon theme styling
*/ */
.fl-neon { .fl-neon {
@@ -78,11 +63,9 @@
font-family: 'Inter', var(--fl-font), sans-serif; /* Prefer Inter font for elegant typography */ font-family: 'Inter', var(--fl-font), sans-serif; /* Prefer Inter font for elegant typography */
will-change: transform, opacity, filter; /* Optimize for animation performance */ will-change: transform, opacity, filter; /* Optimize for animation performance */
overflow: hidden; /* Contain progress bar */ overflow: hidden; /* Contain progress bar */
/* Frosted glass effect */ /* Frosted glass effect */
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
/** /**
* Floating illuminated indicator * Floating illuminated indicator
* Creates a glowing circle that appears to float above the notification * Creates a glowing circle that appears to float above the notification
@@ -98,7 +81,6 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
animation: neonGlow 3s ease-in-out infinite; /* Continuous gentle pulsing */ animation: neonGlow 3s ease-in-out infinite; /* Continuous gentle pulsing */
/* Semi-transparent circular background */ /* Semi-transparent circular background */
&::before { &::before {
content: ''; content: '';
@@ -108,7 +90,6 @@
border-radius: 50%; border-radius: 50%;
opacity: 0.4; /* Subtle background */ opacity: 0.4; /* Subtle background */
} }
/* Solid colored center dot */ /* Solid colored center dot */
&::after { &::after {
content: ''; content: '';
@@ -119,7 +100,6 @@
z-index: 1; /* Ensure dot appears above the glow */ z-index: 1; /* Ensure dot appears above the glow */
} }
} }
/** /**
* Content container * Content container
* Holds message and close button * Holds message and close button
@@ -128,7 +108,6 @@
display: flex; display: flex;
align-items: center; /* Vertically center content */ align-items: center; /* Vertically center content */
} }
/** /**
* Message styling * Message styling
* Main notification text * Main notification text
@@ -139,7 +118,6 @@
line-height: 1.5; /* Comfortable line height */ line-height: 1.5; /* Comfortable line height */
font-weight: 500; /* Medium weight for better readability */ font-weight: 500; /* Medium weight for better readability */
} }
/** /**
* Close button styling * Close button styling
* Circular button with hover effect * Circular button with hover effect
@@ -161,13 +139,11 @@
transition: all 0.2s ease; /* Smooth hover transition */ transition: all 0.2s ease; /* Smooth hover transition */
font-size: 1.2rem; font-size: 1.2rem;
flex-shrink: 0; /* Prevent button from shrinking */ flex-shrink: 0; /* Prevent button from shrinking */
&:hover, &:focus { &:hover, &:focus {
opacity: 1; /* Full opacity on hover/focus */ opacity: 1; /* Full opacity on hover/focus */
background-color: rgba(0, 0, 0, 0.06); /* Subtle background on hover */ background-color: rgba(0, 0, 0, 0.06); /* Subtle background on hover */
} }
} }
/** /**
* Progress bar container * Progress bar container
* Holds the animated progress indicator * Holds the animated progress indicator
@@ -180,7 +156,6 @@
height: 3px; height: 3px;
overflow: hidden; overflow: hidden;
border-radius: 0 0 var(--neon-border-radius) var(--neon-border-radius); /* Match parent corners */ border-radius: 0 0 var(--neon-border-radius) var(--neon-border-radius); /* Match parent corners */
/** /**
* Progress indicator * Progress indicator
* Animated by JavaScript to show remaining time * Animated by JavaScript to show remaining time
@@ -190,7 +165,6 @@
width: 100%; width: 100%;
} }
} }
/** /**
* Type-specific styling for each notification type * Type-specific styling for each notification type
* Each type gets its own color for the indicator and progress bar * Each type gets its own color for the indicator and progress bar
@@ -203,7 +177,6 @@
} }
.fl-progress { background-color: var(--neon-success); } /* Green progress bar */ .fl-progress { background-color: var(--neon-success); } /* Green progress bar */
} }
&.fl-info { &.fl-info {
.fl-icon-box { .fl-icon-box {
color: var(--neon-info); /* Blue glow */ color: var(--neon-info); /* Blue glow */
@@ -212,7 +185,6 @@
} }
.fl-progress { background-color: var(--neon-info); } /* Blue progress bar */ .fl-progress { background-color: var(--neon-info); } /* Blue progress bar */
} }
&.fl-warning { &.fl-warning {
.fl-icon-box { .fl-icon-box {
color: var(--neon-warning); /* Amber glow */ color: var(--neon-warning); /* Amber glow */
@@ -221,7 +193,6 @@
} }
.fl-progress { background-color: var(--neon-warning); } /* Amber progress bar */ .fl-progress { background-color: var(--neon-warning); } /* Amber progress bar */
} }
&.fl-error { &.fl-error {
.fl-icon-box { .fl-icon-box {
color: var(--neon-error); /* Red glow */ color: var(--neon-error); /* Red glow */
@@ -230,43 +201,35 @@
} }
.fl-progress { background-color: var(--neon-error); } /* Red progress bar */ .fl-progress { background-color: var(--neon-error); } /* Red progress bar */
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-icon-box { .fl-icon-box {
left: auto; left: auto;
right: 16px; /* Move icon to right side */ right: 16px; /* Move icon to right side */
} }
.fl-close { .fl-close {
margin-left: 0; margin-left: 0;
margin-right: 16px; /* Swap margins */ margin-right: 16px; /* Swap margins */
} }
.fl-progress { .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
*/ */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
animation: none; /* Disable entrance animation */ animation: none; /* Disable entrance animation */
.fl-icon-box { .fl-icon-box {
animation: none; /* Disable glow animation */ animation: none; /* Disable glow animation */
} }
} }
} }
/**
* Dark mode support * Dark mode support
* Different styling when in dark mode * Different styling when in dark mode
*/ */
@@ -276,7 +239,6 @@ html.fl-dark .fl-neon,
background-color: var(--neon-bg-dark); /* Darker, semi-transparent background */ background-color: var(--neon-bg-dark); /* Darker, semi-transparent background */
color: var(--neon-text-dark); /* Light text */ color: var(--neon-text-dark); /* Light text */
box-shadow: var(--neon-shadow-dark); /* Stronger shadow */ box-shadow: var(--neon-shadow-dark); /* Stronger shadow */
/** /**
* Adjust hover effect for dark mode * Adjust hover effect for dark mode
* Use white instead of black with appropriate opacity * Use white instead of black with appropriate opacity
@@ -1,51 +1,10 @@
/**
* @file PHPFlasher Neon Theme Implementation
* @description Elegant notifications with subtle glowing accents
* @author Younes ENNAJI
*/
import './neon.scss' import './neon.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Neon notification theme for PHPFlasher.
*
* The Neon theme provides an elegant visual style with:
* - Subtle glowing accents based on notification type
* - Floating illuminated circular indicator
* - Frosted glass background with blur effect
* - Refined typography using Inter font
* - Smooth entrance animation with blur transition
* - Gentle pulsing glow effect on the indicator
*
* This theme creates a modern, sophisticated appearance with a touch
* of futuristic design through its subtle illumination effects.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { neonTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('neon', neonTheme);
*
* // Use the theme
* flasher.use('theme.neon').success('Your changes have been saved');
* ```
*/
export const neonTheme = { export const neonTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates an elegant notification with a glowing indicator,
* message content, close button, and progress bar.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,30 +1,4 @@
/**
* @file PHPFlasher Onyx Theme Registration
* @description Registers the Onyx theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { onyxTheme } from './onyx' import { onyxTheme } from './onyx'
/**
* Register the Onyx theme.
*
* This theme provides modern, floating notifications with subtle accent dots
* and a clean, sophisticated design.
*
* The registration makes the theme available under the name 'onyx'.
*
* The Onyx theme can be used by calling:
* ```typescript
* flasher.use('theme.onyx').success('Your changes have been saved');
* ```
*
* Key features:
* - Clean, floating cards with elegant shadows
* - Colored accent dots in the corners
* - Generous rounded corners (1rem radius)
* - Smooth entrance animation with blur transition
* - Minimal design without icons
* - Type-specific colored accents and progress bar
*/
flasher.addTheme('onyx', onyxTheme) flasher.addTheme('onyx', onyxTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Onyx Theme Styles
* @description CSS styling for modern floating notifications with subtle accents
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Onyx Theme * PHPFlasher - Onyx Theme
* *
* Modern notification style with floating appearance and subtle accents. * Modern notification style with floating appearance and subtle accents.
@@ -12,7 +6,6 @@
*/ */
.fl-onyx { .fl-onyx {
/* Theme variables - Define the visual appearance of Onyx notifications */ /* Theme variables - Define the visual appearance of Onyx notifications */
/* Base colors and appearance */ /* Base colors and appearance */
--onyx-bg-light: #ffffff; /* Pure white background in light mode */ --onyx-bg-light: #ffffff; /* Pure white background in light mode */
--onyx-bg-dark: #1e1e1e; /* Dark background in dark mode */ --onyx-bg-dark: #1e1e1e; /* Dark background in dark mode */
@@ -21,15 +14,12 @@
--onyx-shadow: 0 8px 30px rgba(0, 0, 0, 0.12); /* Soft, spread shadow */ --onyx-shadow: 0 8px 30px rgba(0, 0, 0, 0.12); /* Soft, spread shadow */
--onyx-shadow-dark: 0 8px 30px rgba(0, 0, 0, 0.25); /* Deeper shadow for dark mode */ --onyx-shadow-dark: 0 8px 30px rgba(0, 0, 0, 0.25); /* Deeper shadow for dark mode */
--onyx-border-radius: 4px; /* Generous rounded corners */ --onyx-border-radius: 4px; /* Generous rounded corners */
/* Type-specific colors for accent dots and progress bar */ /* Type-specific colors for accent dots and progress bar */
--onyx-success: #10b981; /* Green for success */ --onyx-success: #10b981; /* Green for success */
--onyx-info: #3b82f6; /* Blue for info */ --onyx-info: #3b82f6; /* Blue for info */
--onyx-warning: #f59e0b; /* Amber for warning */ --onyx-warning: #f59e0b; /* Amber for warning */
--onyx-error: #ef4444; /* Red for error */ --onyx-error: #ef4444; /* Red for error */
} }
/**
* Entrance animation for Onyx theme * Entrance animation for Onyx theme
* Combines upward movement, fade, and blur transitions * Combines upward movement, fade, and blur transitions
*/ */
@@ -45,8 +35,6 @@
filter: blur(0); /* End with clear focus */ filter: blur(0); /* End with clear focus */
} }
} }
/**
* Base Onyx theme styling * Base Onyx theme styling
*/ */
.fl-onyx { .fl-onyx {
@@ -61,7 +49,6 @@
will-change: transform, opacity, filter; /* Optimize for animation performance */ will-change: transform, opacity, filter; /* Optimize for animation performance */
border-radius: var(--onyx-border-radius) var(--onyx-border-radius) 0 0; /* Rounded corners */ border-radius: var(--onyx-border-radius) var(--onyx-border-radius) 0 0; /* Rounded corners */
overflow: hidden; /* Contain progress bar */ overflow: hidden; /* Contain progress bar */
/** /**
* Accent dots at the corners * Accent dots at the corners
* Small circular indicators of notification type * Small circular indicators of notification type
@@ -74,19 +61,16 @@
border-radius: 50%; /* Perfectly circular */ border-radius: 50%; /* Perfectly circular */
z-index: 1; /* Ensure dots appear above content */ z-index: 1; /* Ensure dots appear above content */
} }
/* Top-left dot */ /* Top-left dot */
&::before { &::before {
top: 10px; top: 10px;
left: 10px; left: 10px;
} }
/* Bottom-right dot */ /* Bottom-right dot */
&::after { &::after {
bottom: 10px; bottom: 10px;
right: 10px; right: 10px;
} }
/** /**
* Content container * Content container
* Holds message and close button * Holds message and close button
@@ -96,7 +80,6 @@
align-items: center; /* Vertically center content */ align-items: center; /* Vertically center content */
padding-left: 0.4rem; /* Space after the dot */ padding-left: 0.4rem; /* Space after the dot */
} }
/** /**
* Text container * Text container
* Wraps the message * Wraps the message
@@ -105,7 +88,6 @@
flex: 1; /* Take available space */ flex: 1; /* Take available space */
position: relative; position: relative;
} }
/** /**
* Message styling * Message styling
* Main notification text * Main notification text
@@ -116,7 +98,6 @@
font-weight: 400; /* Regular weight for clean appearance */ font-weight: 400; /* Regular weight for clean appearance */
letter-spacing: 0.01rem; /* Slight letter spacing for readability */ letter-spacing: 0.01rem; /* Slight letter spacing for readability */
} }
/** /**
* Close button styling * Close button styling
* Circular button with hover effect * Circular button with hover effect
@@ -138,13 +119,11 @@
justify-content: center; justify-content: center;
width: 1.75rem; /* 28px diameter */ width: 1.75rem; /* 28px diameter */
height: 1.75rem; height: 1.75rem;
&:hover, &:focus { &:hover, &:focus {
opacity: 1; /* Full opacity on hover/focus */ opacity: 1; /* Full opacity on hover/focus */
background-color: rgba(0, 0, 0, 0.05); /* Subtle background on hover */ background-color: rgba(0, 0, 0, 0.05); /* Subtle background on hover */
} }
} }
/** /**
* Progress bar container * Progress bar container
* Holds the animated progress indicator * Holds the animated progress indicator
@@ -156,7 +135,6 @@
bottom: 0; bottom: 0;
height: 3px; /* Thin progress bar */ height: 3px; /* Thin progress bar */
overflow: hidden; overflow: hidden;
/** /**
* Progress indicator * Progress indicator
* Animated by JavaScript to show remaining time * Animated by JavaScript to show remaining time
@@ -167,76 +145,61 @@
transform-origin: left center; /* Animation starts from left */ transform-origin: left center; /* Animation starts from left */
} }
} }
/** /**
* Type-specific styling for each notification type * Type-specific styling for each notification type
* Each type gets its own color for the dots and progress bar * Each type gets its own color for the dots and progress bar
*/ */
&.fl-success { &.fl-success {
&::before, &::after { background-color: var(--onyx-success); } /* Green dots */ &::before, &::after { background-color: var(--onyx-success); } /* Green dots */
.fl-progress-bar .fl-progress { .fl-progress-bar .fl-progress {
background-color: var(--onyx-success); /* Green progress bar */ background-color: var(--onyx-success); /* Green progress bar */
} }
} }
&.fl-info { &.fl-info {
&::before, &::after { background-color: var(--onyx-info); } /* Blue dots */ &::before, &::after { background-color: var(--onyx-info); } /* Blue dots */
.fl-progress-bar .fl-progress { .fl-progress-bar .fl-progress {
background-color: var(--onyx-info); /* Blue progress bar */ background-color: var(--onyx-info); /* Blue progress bar */
} }
} }
&.fl-warning { &.fl-warning {
&::before, &::after { background-color: var(--onyx-warning); } /* Amber dots */ &::before, &::after { background-color: var(--onyx-warning); } /* Amber dots */
.fl-progress-bar .fl-progress { .fl-progress-bar .fl-progress {
background-color: var(--onyx-warning); /* Amber progress bar */ background-color: var(--onyx-warning); /* Amber progress bar */
} }
} }
&.fl-error { &.fl-error {
&::before, &::after { background-color: var(--onyx-error); } /* Red dots */ &::before, &::after { background-color: var(--onyx-error); } /* Red dots */
.fl-progress-bar .fl-progress { .fl-progress-bar .fl-progress {
background-color: var(--onyx-error); /* Red progress bar */ background-color: var(--onyx-error); /* Red progress bar */
} }
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-content { .fl-content {
padding-left: 0; padding-left: 0;
padding-right: 0.4rem; /* Swap padding for RTL */ padding-right: 0.4rem; /* Swap padding for RTL */
} }
.fl-close { .fl-close {
margin-left: 0; margin-left: 0;
margin-right: 1rem; /* Swap margins for RTL */ margin-right: 1rem; /* Swap margins for RTL */
} }
/* Swap dot positions for RTL */ /* Swap dot positions for RTL */
&::before { &::before {
left: auto; left: auto;
right: 10px; right: 10px;
} }
&::after { &::after {
right: auto; right: auto;
left: 10px; left: 10px;
} }
.fl-progress .fl-progress { .fl-progress .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
@@ -245,8 +208,6 @@
animation: none; /* Disable animation */ animation: none; /* Disable animation */
} }
} }
/**
* Dark mode support * Dark mode support
* Different styling when in dark mode * Different styling when in dark mode
*/ */
@@ -256,7 +217,6 @@ html.fl-dark .fl-onyx,
background-color: var(--onyx-bg-dark); /* Dark background */ background-color: var(--onyx-bg-dark); /* Dark background */
color: var(--onyx-text-dark); /* Light text */ color: var(--onyx-text-dark); /* Light text */
box-shadow: var(--onyx-shadow-dark); /* Stronger shadow */ box-shadow: var(--onyx-shadow-dark); /* Stronger shadow */
/** /**
* Adjust hover effect for dark mode * Adjust hover effect for dark mode
* Use white instead of black with appropriate opacity * Use white instead of black with appropriate opacity
@@ -1,51 +1,10 @@
/**
* @file PHPFlasher Onyx Theme Implementation
* @description Modern floating notifications with subtle accent elements
* @author Younes ENNAJI
*/
import './onyx.scss' import './onyx.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Onyx notification theme for PHPFlasher.
*
* The Onyx theme provides a modern, refined visual style with:
* - Clean, floating cards with elegant shadows
* - Small accent dots in the corners indicating notification type
* - Generous rounded corners for a contemporary look
* - Smooth entrance animation with blur transition
* - No icons, maintaining a minimal, focused design
* - Subtle progress indicator at the bottom
*
* This theme creates a sophisticated appearance that integrates
* well with modern interfaces while providing clear status information.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { onyxTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('onyx', onyxTheme);
*
* // Use the theme
* flasher.use('theme.onyx').success('Your changes have been saved');
* ```
*/
export const onyxTheme = { export const onyxTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a clean, modern notification with message content,
* close button, and progress bar. The accent dots are added via CSS.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,15 +1,3 @@
/**
* @file PHPFlasher Progress Bar Styles
* @description Styling for notification progress bars
* @author Younes ENNAJI
*/
/**
* .fl-progress-bar
*
* Container for the progress indicator that shows remaining time.
* This styles the progress bar container across all notification types.
*/
.fl-progress-bar { .fl-progress-bar {
display: flex; display: flex;
height: 0.125em; height: 0.125em;
@@ -20,20 +8,10 @@
overflow: hidden; overflow: hidden;
} }
/**
* Type-specific progress bar styling
* Ensures progress bars match the notification type color scheme
*/
@each $type in success, info, warning, error { @each $type in success, info, warning, error {
.fl-#{$type} .fl-progress-bar { .fl-#{$type} .fl-progress-bar {
background-color: var(--#{$type}-color-light, var(--fl-#{$type}-light)); background-color: var(--#{$type}-color-light, var(--fl-#{$type}-light));
/**
* .fl-progress
*
* The actual progress indicator that animates as time passes.
* Width is controlled by JavaScript.
*/
.fl-progress { .fl-progress {
background-color: var(--#{$type}-color, var(--fl-#{$type})); background-color: var(--#{$type}-color, var(--fl-#{$type}));
width: 100%; width: 100%;
@@ -44,10 +22,6 @@
} }
} }
/**
* RTL support for progress bars
* Ensures animation works correctly in right-to-left layouts
*/
.fl-rtl .fl-progress .fl-progress { .fl-rtl .fl-progress .fl-progress {
transform-origin: right center; transform-origin: right center;
} }
@@ -1,29 +1,4 @@
/**
* @file PHPFlasher Ruby Theme Registration
* @description Registers the Ruby theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { rubyTheme } from './ruby' import { rubyTheme } from './ruby'
/**
* Register the Ruby theme.
*
* This theme provides vibrant notifications with gradient backgrounds,
* gemstone-like shine effects, and elegant animations.
*
* The registration makes the theme available under the name 'ruby'.
*
* The Ruby theme can be used by calling:
* ```typescript
* flasher.use('theme.ruby').success('Your changes have been saved');
* ```
*
* Key features:
* - Rich gradient backgrounds for each notification type
* - Elegant shine animation creating a gemstone-like effect
* - Circular icon container with translucent background
* - Smooth scale animation for entrance
* - Improved hover effects on close button
*/
flasher.addTheme('ruby', rubyTheme) flasher.addTheme('ruby', rubyTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Ruby Theme Styles
* @description CSS styling for vibrant gradient notifications with gemstone effects
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Ruby Theme * PHPFlasher - Ruby Theme
* *
* Vibrant notification style with gradient backgrounds and gemstone effects. * Vibrant notification style with gradient backgrounds and gemstone effects.
@@ -12,21 +6,17 @@
*/ */
.fl-ruby { .fl-ruby {
/* Theme variables - Define the visual appearance of Ruby notifications */ /* Theme variables - Define the visual appearance of Ruby notifications */
/* Base colors and appearance */ /* Base colors and appearance */
--ruby-text: #ffffff; /* White text for good contrast on gradients */ --ruby-text: #ffffff; /* White text for good contrast on gradients */
--ruby-text-dark: #f8fafc; /* Slightly off-white text for dark mode */ --ruby-text-dark: #f8fafc; /* Slightly off-white text for dark mode */
--ruby-border-radius: 4px; /* Rounded corners (18px) */ --ruby-border-radius: 4px; /* Rounded corners (18px) */
--ruby-shadow: 0 10px 25px -3px rgba(0, 0, 0, 0.2); /* Soft shadow for depth */ --ruby-shadow: 0 10px 25px -3px rgba(0, 0, 0, 0.2); /* Soft shadow for depth */
/* Type-specific gradients - rich, vibrant color combinations */ /* Type-specific gradients - rich, vibrant color combinations */
--ruby-success-gradient: linear-gradient(135deg, #059669 0%, #10b981 100%); /* Green gradient */ --ruby-success-gradient: linear-gradient(135deg, #059669 0%, #10b981 100%); /* Green gradient */
--ruby-info-gradient: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); /* Blue gradient */ --ruby-info-gradient: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); /* Blue gradient */
--ruby-warning-gradient: linear-gradient(135deg, #d97706 0%, #f59e0b 100%); /* Amber gradient */ --ruby-warning-gradient: linear-gradient(135deg, #d97706 0%, #f59e0b 100%); /* Amber gradient */
--ruby-error-gradient: linear-gradient(135deg, #b91c1c 0%, #ef4444 100%); /* Red gradient */ --ruby-error-gradient: linear-gradient(135deg, #b91c1c 0%, #ef4444 100%); /* Red gradient */
} }
/**
* Shine animation for Ruby theme * Shine animation for Ruby theme
* Creates a moving highlight effect like light reflecting off a gemstone * Creates a moving highlight effect like light reflecting off a gemstone
*/ */
@@ -44,8 +34,6 @@
opacity: 0; /* Fade out at the end */ opacity: 0; /* Fade out at the end */
} }
} }
/**
* Entrance animation for Ruby theme * Entrance animation for Ruby theme
* Smooth scale-in effect * Smooth scale-in effect
*/ */
@@ -59,8 +47,6 @@
transform: scale(1); /* End at normal size */ transform: scale(1); /* End at normal size */
} }
} }
/**
* Base Ruby theme styling * Base Ruby theme styling
*/ */
.fl-ruby { .fl-ruby {
@@ -74,7 +60,6 @@
font-family: var(--fl-font), serif; font-family: var(--fl-font), serif;
will-change: transform, opacity; /* Optimize for animation performance */ will-change: transform, opacity; /* Optimize for animation performance */
overflow: hidden; /* Contain the shine effect and progress bar */ overflow: hidden; /* Contain the shine effect and progress bar */
/** /**
* Shine effect overlay * Shine effect overlay
* Creates the gemstone-like reflecting light animation * Creates the gemstone-like reflecting light animation
@@ -96,7 +81,6 @@
animation-delay: 1s; /* Delay before first shine */ animation-delay: 1s; /* Delay before first shine */
z-index: 1; /* Above the background, below content */ z-index: 1; /* Above the background, below content */
} }
/** /**
* Content container * Content container
* Holds icon, message and close button * Holds icon, message and close button
@@ -108,7 +92,6 @@
position: relative; position: relative;
z-index: 2; /* Ensure content is above shine effect */ z-index: 2; /* Ensure content is above shine effect */
} }
/** /**
* Circular icon container * Circular icon container
* Translucent white circle holding the icon * Translucent white circle holding the icon
@@ -124,7 +107,6 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
/** /**
* Icon styling * Icon styling
* Icon within the circular container * Icon within the circular container
@@ -135,7 +117,6 @@
color: var(--ruby-text); /* White icon */ color: var(--ruby-text); /* White icon */
background-color: transparent; background-color: transparent;
} }
/** /**
* Text container * Text container
* Holds the message * Holds the message
@@ -143,7 +124,6 @@
.fl-text { .fl-text {
flex: 1; /* Take available space */ flex: 1; /* Take available space */
} }
/** /**
* Message styling * Message styling
* Main notification text * Main notification text
@@ -153,7 +133,6 @@
line-height: 1.5; /* Comfortable line height */ line-height: 1.5; /* Comfortable line height */
font-weight: 500; /* Medium weight for better readability on colored backgrounds */ font-weight: 500; /* Medium weight for better readability on colored backgrounds */
} }
/** /**
* Close button styling * Close button styling
* Circular button with hover effect * Circular button with hover effect
@@ -175,14 +154,12 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
&:hover, &:focus { &:hover, &:focus {
opacity: 1; /* Full opacity on hover/focus */ opacity: 1; /* Full opacity on hover/focus */
background: rgba(255, 255, 255, 0.3); /* Lighter background on hover */ background: rgba(255, 255, 255, 0.3); /* Lighter background on hover */
transform: scale(1.05); /* Subtle grow effect on hover */ transform: scale(1.05); /* Subtle grow effect on hover */
} }
} }
/** /**
* Progress bar container * Progress bar container
* Holds the animated progress indicator * Holds the animated progress indicator
@@ -196,7 +173,6 @@
background-color: rgba(0, 0, 0, 0.1); /* Slightly dark background */ background-color: rgba(0, 0, 0, 0.1); /* Slightly dark background */
overflow: hidden; overflow: hidden;
z-index: 3; /* Above other elements */ z-index: 3; /* Above other elements */
/** /**
* Progress indicator * Progress indicator
* Animated by JavaScript to show remaining time * Animated by JavaScript to show remaining time
@@ -207,7 +183,6 @@
background: rgba(255, 255, 255, 0.4); /* Translucent white progress */ background: rgba(255, 255, 255, 0.4); /* Translucent white progress */
} }
} }
/** /**
* Type-specific styling * Type-specific styling
* Each notification type gets its own gradient background * Each notification type gets its own gradient background
@@ -215,52 +190,42 @@
&.fl-success { &.fl-success {
background: var(--ruby-success-gradient); /* Green gradient */ background: var(--ruby-success-gradient); /* Green gradient */
} }
&.fl-info { &.fl-info {
background: var(--ruby-info-gradient); /* Blue gradient */ background: var(--ruby-info-gradient); /* Blue gradient */
} }
&.fl-warning { &.fl-warning {
background: var(--ruby-warning-gradient); /* Amber gradient */ background: var(--ruby-warning-gradient); /* Amber gradient */
} }
&.fl-error { &.fl-error {
background: var(--ruby-error-gradient); /* Red gradient */ background: var(--ruby-error-gradient); /* Red gradient */
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; direction: rtl;
.fl-icon-circle { .fl-icon-circle {
margin-right: 0; margin-right: 0;
margin-left: 1rem; /* Swap margins for RTL */ margin-left: 1rem; /* Swap margins for RTL */
} }
.fl-close { .fl-close {
margin-left: 0; margin-left: 0;
margin-right: 0.75rem; /* Swap margins for RTL */ margin-right: 0.75rem; /* Swap margins for RTL */
} }
.fl-shine { .fl-shine {
transform: skewX(20deg); /* Reverse shine angle for RTL */ transform: skewX(20deg); /* Reverse shine angle for RTL */
} }
.fl-progress .fl-progress { .fl-progress .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
*/ */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
animation: none; /* Disable entrance animation */ animation: none; /* Disable entrance animation */
.fl-shine { .fl-shine {
display: none; /* Remove shine effect for reduced motion */ display: none; /* Remove shine effect for reduced motion */
} }
@@ -1,51 +1,10 @@
/**
* @file PHPFlasher Ruby Theme Implementation
* @description Vibrant notifications with gradient backgrounds and gemstone effects
* @author Younes ENNAJI
*/
import './ruby.scss' import './ruby.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Ruby notification theme for PHPFlasher.
*
* The Ruby theme provides a vibrant, colorful style with:
* - Rich gradient backgrounds for each notification type
* - Elegant shine animation reminiscent of light reflecting off gemstones
* - Circular icon container with translucent background
* - Clean typography with good contrast on colored backgrounds
* - Smooth scale animation for entrance
* - Semi-transparent progress indicator
*
* This theme creates an eye-catching appearance that draws attention
* while maintaining excellent readability and a premium feel.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { rubyTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('ruby', rubyTheme);
*
* // Use the theme
* flasher.use('theme.ruby').success('Your changes have been saved');
* ```
*/
export const rubyTheme = { export const rubyTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a vibrant, gradient notification with shine effect,
* circular icon container, message content, close button, and progress bar.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,29 +1,4 @@
/**
* @file PHPFlasher Sapphire Theme Registration
* @description Registers the Sapphire theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { sapphireTheme } from './sapphire' import { sapphireTheme } from './sapphire'
/**
* Register the Sapphire theme.
*
* This theme provides modern, glassmorphic notifications with blurred
* backdrop effect and a clean, minimal design.
*
* The registration makes the theme available under the name 'sapphire'.
*
* The Sapphire theme can be used by calling:
* ```typescript
* flasher.use('theme.sapphire').success('Your changes have been saved');
* ```
*
* Key features:
* - Glassmorphic design with backdrop blur effect
* - Minimal interface without icons or close buttons
* - Type-specific colored backgrounds with high transparency
* - Subtle entrance animation with a slight bounce
* - Clean progress indicator at the bottom
*/
flasher.addTheme('sapphire', sapphireTheme) flasher.addTheme('sapphire', sapphireTheme)
@@ -1,38 +1,26 @@
/**
* @file PHPFlasher Sapphire Theme Styles
* @description CSS styling for modern glassmorphic notifications
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Sapphire Theme * PHPFlasher - Sapphire Theme
* *
* Modern glassmorphic notifications with blurred backdrop effect. * Modern glassmorphic notifications with blurred backdrop effect.
* Features clean, minimal design and subtle animations. * Features clean, minimal design and subtle animations.
*/ */
/* Theme-specific variables - override these to customize the sapphire theme */ /* Theme-specific variables - override these to customize the sapphire theme */
.fl-sapphire { .fl-sapphire {
/* Base appearance */ /* Base appearance */
--sapphire-bg-base: rgba(30, 30, 30, 0.9); /* Dark semi-transparent background */ --sapphire-bg-base: rgba(30, 30, 30, 0.9); /* Dark semi-transparent background */
--sapphire-text: #f0f0f0; /* Light text for contrast */ --sapphire-text: #f0f0f0; /* Light text for contrast */
--sapphire-shadow: rgba(0, 0, 0, 0.15); /* Subtle shadow */ --sapphire-shadow: rgba(0, 0, 0, 0.15); /* Subtle shadow */
/* Progress bar colors */ /* Progress bar colors */
--sapphire-progress-bg: rgba(255, 255, 255, 0.2); /* Light translucent background */ --sapphire-progress-bg: rgba(255, 255, 255, 0.2); /* Light translucent background */
--sapphire-progress-fill: rgba(255, 255, 255, 0.9); /* Nearly white progress indicator */ --sapphire-progress-fill: rgba(255, 255, 255, 0.9); /* Nearly white progress indicator */
/* Notification type colors - all with high transparency */ /* Notification type colors - all with high transparency */
--sapphire-success: rgba(16, 185, 129, 0.95); /* Transparent green */ --sapphire-success: rgba(16, 185, 129, 0.95); /* Transparent green */
--sapphire-error: rgba(239, 68, 68, 0.95); /* Transparent red */ --sapphire-error: rgba(239, 68, 68, 0.95); /* Transparent red */
--sapphire-warning: rgba(245, 158, 11, 0.95); /* Transparent amber */ --sapphire-warning: rgba(245, 158, 11, 0.95); /* Transparent amber */
--sapphire-info: rgba(59, 130, 246, 0.95); /* Transparent blue */ --sapphire-info: rgba(59, 130, 246, 0.95); /* Transparent blue */
/* Animation timing */ /* Animation timing */
--sapphire-animation: 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); /* Smooth bounce easing */ --sapphire-animation: 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); /* Smooth bounce easing */
} }
/**
* Entrance animation keyframes * Entrance animation keyframes
* Includes a subtle bounce effect for a more dynamic appearance * Includes a subtle bounce effect for a more dynamic appearance
*/ */
@@ -49,8 +37,6 @@
transform: translateY(0); /* End at natural position */ transform: translateY(0); /* End at natural position */
} }
} }
/**
* Main theme styling * Main theme styling
*/ */
.fl-sapphire { .fl-sapphire {
@@ -66,14 +52,12 @@
font-family: Roboto, var(--fl-font), serif; /* Prefer Roboto font */ font-family: Roboto, var(--fl-font), serif; /* Prefer Roboto font */
border-radius: 4px 4px 0 0; /* Rounded corners */ border-radius: 4px 4px 0 0; /* Rounded corners */
will-change: transform, opacity; /* Optimize for animation performance */ will-change: transform, opacity; /* Optimize for animation performance */
/** /**
* Glassmorphism effect * Glassmorphism effect
* Creates the frosted glass appearance * Creates the frosted glass appearance
*/ */
backdrop-filter: blur(12px); /* Strong blur effect */ backdrop-filter: blur(12px); /* Strong blur effect */
-webkit-backdrop-filter: blur(12px); /* Safari support */ -webkit-backdrop-filter: blur(12px); /* Safari support */
/** /**
* Message styling * Message styling
* Clean, readable text * Clean, readable text
@@ -83,7 +67,6 @@
line-height: 1.4; /* Comfortable line height */ line-height: 1.4; /* Comfortable line height */
color: var(--sapphire-text); /* Consistent text color */ color: var(--sapphire-text); /* Consistent text color */
} }
/** /**
* Progress bar styling * Progress bar styling
* Indicates time remaining before dismissal * Indicates time remaining before dismissal
@@ -97,7 +80,6 @@
background-color: var(--sapphire-progress-bg); /* Semi-transparent background */ background-color: var(--sapphire-progress-bg); /* Semi-transparent background */
overflow: hidden; overflow: hidden;
border-radius: 0 0 0.375em 0.375em; /* Rounded bottom corners */ border-radius: 0 0 0.375em 0.375em; /* Rounded bottom corners */
/** /**
* Progress indicator * Progress indicator
* The actual moving part of the progress bar * The actual moving part of the progress bar
@@ -110,7 +92,6 @@
will-change: transform; /* Optimize animation performance */ will-change: transform; /* Optimize animation performance */
} }
} }
/** /**
* Type-specific colors * Type-specific colors
* Each notification type gets its own semi-transparent background * Each notification type gets its own semi-transparent background
@@ -118,31 +99,25 @@
&.fl-success { &.fl-success {
background-color: var(--sapphire-success); /* Green background */ background-color: var(--sapphire-success); /* Green background */
} }
&.fl-error { &.fl-error {
background-color: var(--sapphire-error); /* Red background */ background-color: var(--sapphire-error); /* Red background */
} }
&.fl-warning { &.fl-warning {
background-color: var(--sapphire-warning); /* Amber background */ background-color: var(--sapphire-warning); /* Amber background */
} }
&.fl-info { &.fl-info {
background-color: var(--sapphire-info); /* Blue background */ background-color: var(--sapphire-info); /* Blue background */
} }
/** /**
* RTL language support * RTL language support
* Right-to-left text direction * Right-to-left text direction
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; /* Right-to-left text */ direction: rtl; /* Right-to-left text */
.fl-progress .fl-progress { .fl-progress .fl-progress {
transform-origin: right center; /* Animation starts from right */ transform-origin: right center; /* Animation starts from right */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
@@ -1,51 +1,10 @@
/**
* @file PHPFlasher Sapphire Theme Implementation
* @description Modern glassmorphic notifications with blurred backdrop effect
* @author Younes ENNAJI
*/
import './sapphire.scss' import './sapphire.scss'
import type { Envelope } from '../../types' import type { Envelope } from '../../types'
/**
* Sapphire notification theme for PHPFlasher.
*
* The Sapphire theme provides a modern, glassmorphic visual style with:
* - Semi-transparent backgrounds with backdrop blur effect
* - Clean, minimalist design without icons or close buttons
* - Subtle bounce animation for entrance
* - Type-specific colored backgrounds with high transparency
* - Elegant progress indicator at the bottom
*
* This theme emphasizes simplicity and modern design trends, creating
* a sophisticated appearance that integrates well with contemporary interfaces.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import { sapphireTheme } from '@flasher/flasher/themes';
*
* // Register the theme (if not already registered)
* flasher.addTheme('sapphire', sapphireTheme);
*
* // Use the theme
* flasher.use('theme.sapphire').success('Your changes have been saved');
* ```
*/
export const sapphireTheme = { export const sapphireTheme = {
/**
* Renders a notification envelope as HTML.
*
* Creates a clean, minimal notification with just the message content
* and progress bar. Unlike most themes, the Sapphire theme intentionally
* omits a close button and icons for a more streamlined appearance.
*
* @param envelope - The notification envelope to render
* @returns HTML string representation of the notification
*/
render: (envelope: Envelope): string => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' const ariaLive = isAlert ? 'assertive' : 'polite'
@@ -1,28 +1,4 @@
/**
* @file PHPFlasher Slack Theme Registration
* @description Registers the Slack theme with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '../../index' import flasher from '../../index'
import { slackTheme } from './slack' 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) flasher.addTheme('slack', slackTheme)
@@ -1,10 +1,4 @@
/**
* @file PHPFlasher Slack Theme Styles
* @description CSS styling for Slack-inspired notifications
* @author Younes ENNAJI
*/ */
/**
* PHPFlasher - Slack Theme * PHPFlasher - Slack Theme
* *
* Familiar notifications inspired by Slack's popular messaging platform. * Familiar notifications inspired by Slack's popular messaging platform.
@@ -12,7 +6,6 @@
*/ */
.fl-slack { .fl-slack {
/* Theme variables - Define the visual appearance of Slack notifications */ /* Theme variables - Define the visual appearance of Slack notifications */
/* Base colors and appearance */ /* Base colors and appearance */
--slack-bg-light: #ffffff; /* White background in light mode */ --slack-bg-light: #ffffff; /* White background in light mode */
--slack-bg-dark: #1a1d21; /* Dark background in dark mode (Slack dark) */ --slack-bg-dark: #1a1d21; /* Dark background in dark mode (Slack dark) */
@@ -27,18 +20,14 @@
--slack-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); /* Subtle shadow in light mode */ --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-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 */ --slack-avatar-size: 36px; /* Size of avatar/icon container */
/* Type-specific colors matching Slack's color scheme */ /* Type-specific colors matching Slack's color scheme */
--slack-success: #2bac76; /* Green */ --slack-success: #2bac76; /* Green */
--slack-info: #1264a3; /* Blue */ --slack-info: #1264a3; /* Blue */
--slack-warning: #e8912d; /* Orange */ --slack-warning: #e8912d; /* Orange */
--slack-error: #e01e5a; /* Red/Pink */ --slack-error: #e01e5a; /* Red/Pink */
/* Animation timing */ /* Animation timing */
--slack-animation-duration: 150ms; /* Quick animation for responsive feel */ --slack-animation-duration: 150ms; /* Quick animation for responsive feel */
} }
/**
* Simple fade in animation * Simple fade in animation
* Quick and subtle to match Slack's responsive feel * Quick and subtle to match Slack's responsive feel
*/ */
@@ -50,8 +39,6 @@
opacity: 1; /* End fully visible */ opacity: 1; /* End fully visible */
} }
} }
/**
* Base Slack theme styling * Base Slack theme styling
*/ */
.fl-slack { .fl-slack {
@@ -59,7 +46,6 @@
margin: 4px 0; /* Minimal spacing between messages */ margin: 4px 0; /* Minimal spacing between messages */
font-family: "Lato", "Slack-Lato", "Helvetica Neue", "Helvetica", sans-serif; /* Slack's font stack */ 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 */ animation: slackFadeIn var(--slack-animation-duration) ease-out; /* Quick fade in */
/** /**
* Message bubble * Message bubble
* The main container that resembles a Slack message * The main container that resembles a Slack message
@@ -74,12 +60,10 @@
transition: background-color 0.1s ease; /* Smooth hover transition */ transition: background-color 0.1s ease; /* Smooth hover transition */
border: 1px solid var(--slack-border-light); /* Subtle border */ border: 1px solid var(--slack-border-light); /* Subtle border */
box-shadow: var(--slack-shadow); /* Slight shadow for depth */ box-shadow: var(--slack-shadow); /* Slight shadow for depth */
&:hover { &:hover {
background-color: var(--slack-hover-light); /* Background change on hover */ background-color: var(--slack-hover-light); /* Background change on hover */
} }
} }
/** /**
* Avatar container * Avatar container
* Colored square that holds the type icon * Colored square that holds the type icon
@@ -95,7 +79,6 @@
justify-content: center; justify-content: center;
background-color: currentColor; /* Uses the type color */ background-color: currentColor; /* Uses the type color */
} }
/** /**
* Type icon * Type icon
* Symbol within the colored avatar * Symbol within the colored avatar
@@ -105,7 +88,6 @@
font-weight: bold; /* Bold symbol */ font-weight: bold; /* Bold symbol */
font-size: 16px; /* Icon size */ font-size: 16px; /* Icon size */
} }
/** /**
* Message content container * Message content container
* Holds the message text * Holds the message text
@@ -114,7 +96,6 @@
flex: 1; /* Take available space */ flex: 1; /* Take available space */
min-width: 0; /* Allows text to wrap */ min-width: 0; /* Allows text to wrap */
} }
/** /**
* Message text styling * Message text styling
* Follows Slack's text appearance * Follows Slack's text appearance
@@ -124,7 +105,6 @@
line-height: 1.46668; /* Slack's line height */ line-height: 1.46668; /* Slack's line height */
word-break: break-word; /* Allows breaking long words */ word-break: break-word; /* Allows breaking long words */
} }
/** /**
* Actions container * Actions container
* Holds the close button that appears on hover * Holds the close button that appears on hover
@@ -137,7 +117,6 @@
opacity: 0; /* Transparent by default */ opacity: 0; /* Transparent by default */
transition: opacity 0.1s ease; /* Smooth fade in/out */ transition: opacity 0.1s ease; /* Smooth fade in/out */
} }
/** /**
* Show actions on hover * Show actions on hover
* Makes the close button appear when hovering the message * Makes the close button appear when hovering the message
@@ -146,7 +125,6 @@
visibility: visible; /* Show on hover */ visibility: visible; /* Show on hover */
opacity: 1; /* Fade in */ opacity: 1; /* Fade in */
} }
/** /**
* Close button styling * Close button styling
* Simple button with hover effect * Simple button with hover effect
@@ -161,13 +139,11 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
&:hover { &:hover {
color: var(--slack-text-light); /* Darker on hover */ color: var(--slack-text-light); /* Darker on hover */
background-color: var(--slack-hover-light); /* Light background on hover */ background-color: var(--slack-hover-light); /* Light background on hover */
} }
} }
/** /**
* Type-specific styling * Type-specific styling
* Each notification type gets its own colored avatar * Each notification type gets its own colored avatar
@@ -177,52 +153,43 @@
color: var(--slack-success); /* Green avatar */ color: var(--slack-success); /* Green avatar */
} }
} }
&.fl-info { &.fl-info {
.fl-avatar { .fl-avatar {
color: var(--slack-info); /* Blue avatar */ color: var(--slack-info); /* Blue avatar */
} }
} }
&.fl-warning { &.fl-warning {
.fl-avatar { .fl-avatar {
color: var(--slack-warning); /* Orange avatar */ color: var(--slack-warning); /* Orange avatar */
} }
} }
&.fl-error { &.fl-error {
.fl-avatar { .fl-avatar {
color: var(--slack-error); /* Red avatar */ color: var(--slack-error); /* Red avatar */
} }
} }
/** /**
* RTL Support * RTL Support
* Right-to-left language direction support * Right-to-left language direction support
*/ */
&.fl-rtl { &.fl-rtl {
direction: rtl; /* Right-to-left text */ direction: rtl; /* Right-to-left text */
.fl-avatar { .fl-avatar {
margin-right: 0; margin-right: 0;
margin-left: 8px; /* Swap margins */ margin-left: 8px; /* Swap margins */
} }
.fl-username { .fl-username {
margin-right: 0; margin-right: 0;
margin-left: 4px; /* Swap margins */ margin-left: 4px; /* Swap margins */
} }
.fl-actions { .fl-actions {
right: auto; right: auto;
left: 6px; /* Move to top-left */ left: 6px; /* Move to top-left */
} }
.fl-slack-message { .fl-slack-message {
padding: 8px 8px 8px 20px; /* Swap padding */ padding: 8px 8px 8px 20px; /* Swap padding */
} }
} }
/** /**
* Accessibility * Accessibility
* Respects reduced motion preferences * Respects reduced motion preferences
@@ -231,8 +198,6 @@
animation: none; /* Disable animation */ animation: none; /* Disable animation */
} }
} }
/**
* Dark mode support * Dark mode support
* Slack-like dark theme * Slack-like dark theme
*/ */
@@ -244,15 +209,12 @@ html.fl-dark .fl-slack,
color: var(--slack-text-dark); /* Light text */ color: var(--slack-text-dark); /* Light text */
border-color: var(--slack-border-dark); /* Darker border */ border-color: var(--slack-border-dark); /* Darker border */
box-shadow: var(--slack-shadow-dark); /* Stronger shadow */ box-shadow: var(--slack-shadow-dark); /* Stronger shadow */
&:hover { &:hover {
background-color: var(--slack-hover-dark); /* Dark hover state */ background-color: var(--slack-hover-dark); /* Dark hover state */
} }
} }
.fl-close { .fl-close {
color: var(--slack-text-secondary-dark); /* Lighter gray icon */ color: var(--slack-text-secondary-dark); /* Lighter gray icon */
&:hover { &:hover {
color: var(--slack-text-dark); /* White on hover */ color: var(--slack-text-dark); /* White on hover */
background-color: var(--slack-hover-dark); /* Dark background on hover */ background-color: var(--slack-hover-dark); /* Dark background on hover */
@@ -1,60 +1,14 @@
/**
* @file PHPFlasher Slack Theme Implementation
* @description Notifications styled after Slack's messaging interface
* @author Younes ENNAJI
*/
import './slack.scss' import './slack.scss'
import type { Envelope } from '../../types' 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 = { 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 => { render: (envelope: Envelope): string => {
const { type, message } = envelope const { type, message } = envelope
// Set appropriate ARIA roles based on notification type
const isAlert = type === 'error' || type === 'warning' const isAlert = type === 'error' || type === 'warning'
const role = isAlert ? 'alert' : 'status' const role = isAlert ? 'alert' : 'status'
const ariaLive = isAlert ? 'assertive' : 'polite' 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 = () => { const getTypeIcon = () => {
switch (type) { switch (type) {
case 'success': case 'success':
+1 -46
View File
@@ -1,15 +1,3 @@
/**
* @file PHPFlasher Wrapper Styles
* @description Positioning and layout for notification containers
* @author Younes ENNAJI
*/
/**
* .fl-wrapper
*
* The main container that holds notifications.
* Controls position, stacking, and transition effects.
*/
.fl-wrapper { .fl-wrapper {
position: fixed; position: fixed;
z-index: 1000; z-index: 1000;
@@ -17,12 +5,8 @@
transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1); transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
pointer-events: none; pointer-events: none;
/**
* Child notification containers
* Each notification inherits positioning from its wrapper
*/
.fl-container { .fl-container {
pointer-events: auto; // Allow interactions with the notification pointer-events: auto;
transition: transform 0.4s cubic-bezier(0.23, 1, 0.32, 1), transition: transform 0.4s cubic-bezier(0.23, 1, 0.32, 1),
opacity 0.4s cubic-bezier(0.23, 1, 0.32, 1); opacity 0.4s cubic-bezier(0.23, 1, 0.32, 1);
opacity: 0; opacity: 0;
@@ -34,14 +18,10 @@
} }
} }
/**
* Stacking behavior for multiple notifications
*/
&.fl-stacked .fl-container:not(:last-child) { &.fl-stacked .fl-container:not(:last-child) {
margin-bottom: 0.5em; margin-bottom: 0.5em;
} }
// Position variations - vertical positioning
&[data-position^="top-"] { &[data-position^="top-"] {
top: 0.75em; top: 0.75em;
} }
@@ -54,7 +34,6 @@
top: 50%; top: 50%;
} }
// Position variations - horizontal positioning
&[data-position$="-right"] { &[data-position$="-right"] {
right: 0.75em; right: 0.75em;
} }
@@ -67,10 +46,6 @@
left: 50%; left: 50%;
} }
/**
* Complex positioning behaviors for different positions
*/
// Compound transforms for center row
&[data-position="center-left"] { &[data-position="center-left"] {
transform: translateY(-50%); transform: translateY(-50%);
@@ -91,16 +66,13 @@
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
.fl-container { .fl-container {
// Different initial state for center-center to avoid conflicts
transform: translate(0, 0) scale(0.5); transform: translate(0, 0) scale(0.5);
opacity: 0; opacity: 0;
// Slightly longer transition for center-center
transition: transform 0.5s cubic-bezier(0.23, 1, 0.32, 1), transition: transform 0.5s cubic-bezier(0.23, 1, 0.32, 1),
opacity 0.5s cubic-bezier(0.23, 1, 0.32, 1); opacity 0.5s cubic-bezier(0.23, 1, 0.32, 1);
} }
} }
// Standard positioning transforms
&[data-position="top-center"], &[data-position="top-center"],
&[data-position="bottom-center"] { &[data-position="bottom-center"] {
transform: translateX(-50%); transform: translateX(-50%);
@@ -124,10 +96,6 @@
transform: translateX(-110%); transform: translateX(-110%);
} }
/**
* Responsive adjustments
* Ensure notifications display well on all screen sizes
*/
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
width: 85%; width: 85%;
left: 7.5%; left: 7.5%;
@@ -140,7 +108,6 @@
right: auto; right: auto;
} }
// Keep vertical transforms but normalize horizontal ones
&[data-position^="top-"] { &[data-position^="top-"] {
transform: translateX(-50%); transform: translateX(-50%);
top: 0.75em; top: 0.75em;
@@ -163,9 +130,6 @@
right: 5%; right: 5%;
} }
/**
* Dark mode support
*/
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
&.fl-auto-dark .fl-container { &.fl-auto-dark .fl-container {
box-shadow: var(--fl-shadow-dark, 0 4px 12px rgba(0, 0, 0, 0.35)); box-shadow: var(--fl-shadow-dark, 0 4px 12px rgba(0, 0, 0, 0.35));
@@ -173,10 +137,6 @@
} }
} }
/**
* Dismissal animations
* Control how notifications animate out when closed
*/
.fl-wrapper .fl-container.fl-dismissing { .fl-wrapper .fl-container.fl-dismissing {
opacity: 0; opacity: 0;
transition: all 0.3s ease-out; transition: all 0.3s ease-out;
@@ -197,7 +157,6 @@
&[data-position="center-center"] { &[data-position="center-center"] {
transform: scale(0.5); transform: scale(0.5);
// Slightly longer dismiss time for center-center
transition: opacity 0.4s ease-out, transform 0.4s ease-out; transition: opacity 0.4s ease-out, transform 0.4s ease-out;
} }
@@ -214,10 +173,6 @@
} }
} }
/**
* Accessibility support
* Respect reduced motion preferences
*/
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.fl-wrapper, .fl-wrapper,
.fl-wrapper .fl-container { .fl-wrapper .fl-container {
@@ -1,55 +1,7 @@
/**
* @file SweetAlert Plugin Entry Point
* @description Registers the SweetAlert plugin with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '@flasher/flasher' import flasher from '@flasher/flasher'
import SweetAlertPlugin from './sweetalert' import SweetAlertPlugin from './sweetalert'
/**
* Create and register the SweetAlert plugin with PHPFlasher.
*
* This enables using SweetAlert for displaying notifications through
* the PHPFlasher API.
*
* @example
* ```typescript
* // With the plugin already registered
* import flasher from '@flasher/flasher';
*
* // Basic success message
* flasher.use('sweetalert').success('Operation completed');
*
* // Confirmation dialog
* flasher.use('sweetalert').warning('Are you sure?', 'Confirmation', {
* showCancelButton: true,
* confirmButtonText: 'Yes, delete it!',
* cancelButtonText: 'Cancel'
* });
*
* // Listen for user interaction results
* window.addEventListener('flasher:sweetalert:promise', (event) => {
* const { promise, envelope } = event.detail;
* if (promise.isConfirmed) {
* // User clicked confirm button
* console.log('User confirmed', envelope);
* }
* });
* ```
*/
const sweetalert = new SweetAlertPlugin() const sweetalert = new SweetAlertPlugin()
flasher.addPlugin('sweetalert', sweetalert) flasher.addPlugin('sweetalert', sweetalert)
/**
* Export the SweetAlert plugin instance.
*
* This allows direct access to the plugin if needed.
*
* @example
* ```typescript
* import sweetalert from '@flasher/flasher-sweetalert';
*
* sweetalert.success('Operation completed');
* ```
*/
export default sweetalert export default sweetalert
@@ -1,42 +1,7 @@
/**
* @file Toastr Plugin Entry Point
* @description Registers the Toastr plugin with PHPFlasher
* @author Younes ENNAJI
*/
import flasher from '@flasher/flasher' import flasher from '@flasher/flasher'
import ToastrPlugin from './toastr' import ToastrPlugin from './toastr'
/**
* Create and register the Toastr plugin with PHPFlasher.
*
* This enables using Toastr for displaying notifications through
* the PHPFlasher API.
*
* @example
* ```typescript
* // With the plugin already registered
* import flasher from '@flasher/flasher';
*
* // Basic usage
* flasher.use('toastr').success('Operation completed');
*
* // With a title
* flasher.use('toastr').error('Connection failed', 'Error');
* ```
*/
const toastrPlugin = new ToastrPlugin() const toastrPlugin = new ToastrPlugin()
flasher.addPlugin('toastr', toastrPlugin) flasher.addPlugin('toastr', toastrPlugin)
/**
* Export the Toastr plugin instance.
*
* This allows direct access to the plugin if needed.
*
* @example
* ```typescript
* import toastr from '@flasher/flasher-toastr';
*
* toastr.success('Operation completed');
* ```
*/
export default toastrPlugin export default toastrPlugin
+2 -56
View File
@@ -1,49 +1,14 @@
/**
* @file Toastr Plugin Implementation
* @description PHPFlasher integration with the Toastr notification library
* @author Younes ENNAJI
*/
import { AbstractPlugin } from '@flasher/flasher/dist/plugin' import { AbstractPlugin } from '@flasher/flasher/dist/plugin'
import type { Envelope, Options } from '@flasher/flasher/dist/types' import type { Envelope, Options } from '@flasher/flasher/dist/types'
import toastr from 'toastr' import toastr from 'toastr'
/**
* Plugin implementation for Toastr notification library.
*
* The ToastrPlugin integrates the popular Toastr library with PHPFlasher,
* allowing for customizable toast notifications in various positions.
*
* This plugin requires jQuery and Toastr to be loaded in the page. These
* dependencies are automatically loaded by the PHP plugin's getScripts method.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import ToastrPlugin from '@flasher/flasher-toastr';
*
* // Register the plugin
* flasher.addPlugin('toastr', new ToastrPlugin());
*
* // Show a notification
* flasher.use('toastr').success('Operation completed');
* ```
*/
export default class ToastrPlugin extends AbstractPlugin { export default class ToastrPlugin extends AbstractPlugin {
/**
* Creates Toastr notifications from envelopes.
*
* This method transforms PHPFlasher envelopes into Toastr notifications
* and displays them using the Toastr library.
*
* @param envelopes - Array of notification envelopes to render
*/
public renderEnvelopes(envelopes: Envelope[]): void { public renderEnvelopes(envelopes: Envelope[]): void {
if (!envelopes?.length) { if (!envelopes?.length) {
return return
} }
// Check for dependencies
if (!this.isDependencyAvailable()) { if (!this.isDependencyAvailable()) {
return return
} }
@@ -52,10 +17,8 @@ export default class ToastrPlugin extends AbstractPlugin {
try { try {
const { message, title, type, options } = envelope const { message, title, type, options } = envelope
// Display the toast notification
const instance = toastr[type as ToastrType](message, title, options as ToastrOptions) const instance = toastr[type as ToastrType](message, title, options as ToastrOptions)
// Handle Turbo/Hotwire compatibility
if (instance && instance.parent) { if (instance && instance.parent) {
try { try {
const parent = instance.parent() const parent = instance.parent()
@@ -72,25 +35,15 @@ export default class ToastrPlugin extends AbstractPlugin {
}) })
} }
/**
* Apply global options to Toastr.
*
* This method configures the Toastr library with the provided options,
* which will affect all subsequently created notifications.
*
* @param options - Configuration options for Toastr
*/
public renderOptions(options: Options): void { public renderOptions(options: Options): void {
// Check for dependencies
if (!this.isDependencyAvailable()) { if (!this.isDependencyAvailable()) {
return return
} }
try { try {
// Apply default options and merge with provided options
toastr.options = { toastr.options = {
timeOut: (options.timeOut || 10000) as number, // Default 10 seconds timeOut: (options.timeOut || 10000) as number,
progressBar: (options.progressBar || true) as boolean, // Show progress bar by default progressBar: (options.progressBar || true) as boolean,
...options, ...options,
} as ToastrOptions } as ToastrOptions
} catch (error) { } catch (error) {
@@ -98,14 +51,7 @@ export default class ToastrPlugin extends AbstractPlugin {
} }
} }
/**
* Check if jQuery and Toastr dependencies are available
*
* @returns True if dependencies are available
* @private
*/
private isDependencyAvailable(): boolean { private isDependencyAvailable(): boolean {
// Check for jQuery (handle both window.jQuery and window.$ usage)
const jQuery = window.jQuery || window.$ const jQuery = window.jQuery || window.$
if (!jQuery) { if (!jQuery) {