Display: Add support for color theme per access_url & custom logo - refs BT#21621 #5578
@ -0,0 +1,50 @@ |
|||||||
|
<script setup> |
||||||
|
import BaseSelect from "../basecomponents/BaseSelect.vue" |
||||||
|
import { ref } from "vue" |
||||||
|
import themeService from "../../services/colorThemeService" |
||||||
|
import { useI18n } from "vue-i18n" |
||||||
|
import { useNotification } from "../../composables/notification" |
||||||
|
|
||||||
|
const modelValue = defineModel({ |
||||||
|
required: true, |
||||||
|
type: Object, |
||||||
|
}) |
||||||
|
|
||||||
|
const { t } = useI18n() |
||||||
|
const { showErrorNotification } = useNotification() |
||||||
|
|
||||||
|
const serverThemes = ref([]) |
||||||
|
const isServerThemesLoading = ref(true) |
||||||
|
|
||||||
|
const loadThemes = async () => { |
||||||
|
try { |
||||||
|
const { items } = await themeService.findAllByCurrentUrl() |
||||||
|
|
||||||
|
serverThemes.value = items.map((accessUrlRelColorTheme) => accessUrlRelColorTheme.colorTheme) |
||||||
|
|
||||||
|
modelValue.value = items.find((accessUrlRelColorTheme) => accessUrlRelColorTheme.active)?.colorTheme["@id"] |
||||||
|
} catch (e) { |
||||||
|
showErrorNotification(t("We could not retrieve the themes")) |
||||||
|
} finally { |
||||||
|
isServerThemesLoading.value = false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
defineExpose({ |
||||||
|
loadThemes, |
||||||
|
}) |
||||||
|
|
||||||
|
loadThemes() |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<BaseSelect |
||||||
|
v-model="modelValue" |
||||||
|
:is-loading="isServerThemesLoading" |
||||||
|
:label="t('Color theme selected')" |
||||||
|
:options="serverThemes" |
||||||
|
allow-clear |
||||||
|
option-label="title" |
||||||
|
option-value="@id" |
||||||
|
/> |
||||||
|
</template> |
@ -0,0 +1,98 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Chamilo\CoreBundle\Entity; |
||||||
|
|
||||||
|
use ApiPlatform\Metadata\ApiResource; |
||||||
|
use ApiPlatform\Metadata\GetCollection; |
||||||
|
use ApiPlatform\Metadata\Post; |
||||||
|
use Chamilo\CoreBundle\Repository\AccessUrlRelColorThemeRepository; |
||||||
|
use Chamilo\CoreBundle\State\AccessUrlRelColorThemeStateProcessor; |
||||||
|
use Chamilo\CoreBundle\State\AccessUrlRelColorThemeStateProvider; |
||||||
|
use Doctrine\ORM\Mapping as ORM; |
||||||
|
use Gedmo\Timestampable\Traits\TimestampableEntity; |
||||||
|
use Symfony\Component\Serializer\Attribute\Groups; |
||||||
|
|
||||||
|
#[ApiResource( |
||||||
|
operations: [ |
||||||
|
new Post(), |
||||||
|
new GetCollection(), |
||||||
|
], |
||||||
|
normalizationContext: [ |
||||||
|
'groups' => ['access_url_rel_color_theme:read'], |
||||||
|
], |
||||||
|
denormalizationContext: [ |
||||||
|
'groups' => ['access_url_rel_color_theme:write'], |
||||||
|
], |
||||||
|
paginationEnabled: false, |
||||||
|
security: "is_granted('ROLE_ADMIN')", |
||||||
|
provider: AccessUrlRelColorThemeStateProvider::class, |
||||||
|
processor: AccessUrlRelColorThemeStateProcessor::class, |
||||||
|
)] |
||||||
|
#[ORM\Entity(repositoryClass: AccessUrlRelColorThemeRepository::class)] |
||||||
|
class AccessUrlRelColorTheme |
||||||
|
{ |
||||||
|
use TimestampableEntity; |
||||||
|
|
||||||
|
#[ORM\Id] |
||||||
|
#[ORM\GeneratedValue] |
||||||
|
#[ORM\Column] |
||||||
|
private ?int $id = null; |
||||||
|
|
||||||
|
#[ORM\ManyToOne(inversedBy: 'colorThemes')] |
||||||
|
#[ORM\JoinColumn(nullable: false)] |
||||||
|
private ?AccessUrl $url = null; |
||||||
|
|
||||||
|
#[Groups(['access_url_rel_color_theme:write', 'access_url_rel_color_theme:read'])] |
||||||
|
#[ORM\ManyToOne(inversedBy: 'urls')] |
||||||
|
#[ORM\JoinColumn(nullable: false)] |
||||||
|
private ?ColorTheme $colorTheme = null; |
||||||
|
|
||||||
|
#[Groups(['access_url_rel_color_theme:read'])] |
||||||
|
#[ORM\Column] |
||||||
|
private bool $active = false; |
||||||
|
|
||||||
|
public function getId(): ?int |
||||||
|
{ |
||||||
|
return $this->id; |
||||||
|
} |
||||||
|
|
||||||
|
public function getUrl(): ?AccessUrl |
||||||
|
{ |
||||||
|
return $this->url; |
||||||
|
} |
||||||
|
|
||||||
|
public function setUrl(?AccessUrl $url): static |
||||||
|
{ |
||||||
|
$this->url = $url; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function getColorTheme(): ?ColorTheme |
||||||
|
{ |
||||||
|
return $this->colorTheme; |
||||||
|
} |
||||||
|
|
||||||
|
public function setColorTheme(?ColorTheme $colorTheme): static |
||||||
|
{ |
||||||
|
$this->colorTheme = $colorTheme; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function isActive(): bool |
||||||
|
{ |
||||||
|
return $this->active; |
||||||
|
} |
||||||
|
|
||||||
|
public function setActive(bool $active): static |
||||||
|
{ |
||||||
|
$this->active = $active; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Chamilo\CoreBundle\Migrations\Schema\V200; |
||||||
|
|
||||||
|
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo; |
||||||
|
use Doctrine\DBAL\Schema\Schema; |
||||||
|
use Symfony\Component\Filesystem\Filesystem; |
||||||
|
use Symfony\Component\Finder\Finder; |
||||||
|
|
||||||
|
class Version20240704185300 extends AbstractMigrationChamilo |
||||||
|
{ |
||||||
|
public function getDescription(): string |
||||||
|
{ |
||||||
|
return "Fix stylesheet and theme settings and move theme directory during development"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @inheritDoc |
||||||
|
*/ |
||||||
|
public function up(Schema $schema): void |
||||||
|
{ |
||||||
|
$this->addSql("DELETE FROM settings WHERE variable IN ('stylesheets', 'theme')"); |
||||||
|
|
||||||
|
$kernel = $this->container->get('kernel'); |
||||||
|
$rootPath = $kernel->getProjectDir(); |
||||||
|
|
||||||
|
$themeDirectory = $rootPath.'/var/theme'; |
||||||
|
$themesDirectory = $rootPath.'/var/themes'; |
||||||
|
|
||||||
|
$finder = new Finder(); |
||||||
|
$filesystem = new Filesystem(); |
||||||
|
|
||||||
|
$finder->directories()->in($themeDirectory)->depth('== 0'); |
||||||
|
|
||||||
|
foreach ($finder as $entry) { |
||||||
|
if ($entry->isDir()) { |
||||||
|
error_log( |
||||||
|
sprintf( |
||||||
|
"Moving theme directory: %s %s", |
||||||
|
$entry->getRealPath(), |
||||||
|
$themesDirectory.'/' |
||||||
|
) |
||||||
|
); |
||||||
|
$filesystem->rename($entry->getRealPath(), $themesDirectory.'/'.$entry->getRelativePathname()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Chamilo\CoreBundle\Repository; |
||||||
|
|
||||||
|
use Chamilo\CoreBundle\Entity\AccessUrlRelColorTheme; |
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; |
||||||
|
use Doctrine\Persistence\ManagerRegistry; |
||||||
|
|
||||||
|
/** |
||||||
|
* @extends ServiceEntityRepository<AccessUrlRelColorTheme> |
||||||
|
* |
||||||
|
* @method AccessUrlRelColorTheme|null find($id, $lockMode = null, $lockVersion = null) |
||||||
|
* @method AccessUrlRelColorTheme|null findOneBy(array $criteria, array $orderBy = null) |
||||||
|
* @method AccessUrlRelColorTheme[] findAll() |
||||||
|
* @method AccessUrlRelColorTheme[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) |
||||||
|
*/ |
||||||
|
final class AccessUrlRelColorThemeRepository extends ServiceEntityRepository |
||||||
|
{ |
||||||
|
public function __construct(ManagerRegistry $registry) |
||||||
|
{ |
||||||
|
parent::__construct($registry, AccessUrlRelColorTheme::class); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Chamilo\CoreBundle\ServiceHelper; |
||||||
|
|
||||||
|
use Chamilo\CoreBundle\Settings\SettingsManager; |
||||||
|
use Chamilo\CourseBundle\Settings\SettingsCourseManager; |
||||||
|
use League\Flysystem\FilesystemException; |
||||||
|
use League\Flysystem\FilesystemOperator; |
||||||
|
use Symfony\Component\DependencyInjection\Attribute\Autowire; |
||||||
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
||||||
|
use Symfony\Component\Routing\RouterInterface; |
||||||
|
|
||||||
|
final class ThemeHelper |
||||||
|
{ |
||||||
|
public const DEFAULT_THEME = 'chamilo'; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
private readonly AccessUrlHelper $accessUrlHelper, |
||||||
|
private readonly SettingsManager $settingsManager, |
||||||
|
private readonly UserHelper $userHelper, |
||||||
|
private readonly CidReqHelper $cidReqHelper, |
||||||
|
private readonly SettingsCourseManager $settingsCourseManager, |
||||||
|
private readonly RouterInterface $router, |
||||||
|
#[Autowire(service: 'oneup_flysystem.themes_filesystem')] private readonly FilesystemOperator $filesystem, |
||||||
|
) {} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the name of the color theme configured to be applied on the current page. |
||||||
|
* The returned name depends on the platform, course or user settings. |
||||||
|
*/ |
||||||
|
public function getVisualTheme(): string |
||||||
|
{ |
||||||
|
static $visualTheme; |
||||||
|
|
||||||
|
global $lp_theme_css; |
||||||
|
|
||||||
|
if (isset($visualTheme)) { |
||||||
|
return $visualTheme; |
||||||
|
} |
||||||
|
|
||||||
|
$accessUrl = $this->accessUrlHelper->getCurrent(); |
||||||
|
|
||||||
|
$visualTheme = $accessUrl->getActiveColorTheme()?->getColorTheme()->getSlug(); |
||||||
|
|
||||||
|
if ('true' == $this->settingsManager->getSetting('profile.user_selected_theme')) { |
||||||
|
$visualTheme = $this->userHelper->getCurrent()?->getTheme(); |
||||||
|
} |
||||||
|
|
||||||
|
if ('true' == $this->settingsManager->getSetting('course.allow_course_theme')) { |
||||||
|
$course = $this->cidReqHelper->getCourseEntity(); |
||||||
|
|
||||||
|
if ($course) { |
||||||
|
$this->settingsCourseManager->setCourse($course); |
||||||
|
|
||||||
|
$visualTheme = $this->settingsCourseManager->getCourseSettingValue('course_theme'); |
||||||
|
|
||||||
|
if (1 === (int) $this->settingsCourseManager->getCourseSettingValue('allow_learning_path_theme')) { |
||||||
|
$visualTheme = $lp_theme_css; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (empty($visualTheme)) { |
||||||
|
return self::DEFAULT_THEME; |
||||||
|
} |
||||||
|
|
||||||
|
return $visualTheme; |
||||||
|
} |
||||||
|
|
||||||
|
public function getThemeAssetUrl(string $path, bool $absolute = false): string |
||||||
|
{ |
||||||
|
$themeName = $this->getVisualTheme(); |
||||||
|
|
||||||
|
try { |
||||||
|
if (!$this->filesystem->fileExists($themeName.DIRECTORY_SEPARATOR.$path)) { |
||||||
|
return ''; |
||||||
|
} |
||||||
|
} catch (FilesystemException) { |
||||||
|
return ''; |
||||||
|
} |
||||||
|
|
||||||
|
return $this->router->generate( |
||||||
|
'theme_asset', |
||||||
|
['name' => $themeName, 'path' => $path], |
||||||
|
$absolute ? UrlGeneratorInterface::ABSOLUTE_URL : UrlGeneratorInterface::ABSOLUTE_PATH |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function getThemeAssetLinkTag(string $path, bool $absoluteUrl = false): string |
||||||
|
{ |
||||||
|
$url = $this->getThemeAssetUrl($path, $absoluteUrl); |
||||||
|
|
||||||
|
if (empty($url)) { |
||||||
|
return ''; |
||||||
|
} |
||||||
|
|
||||||
|
return sprintf('<link rel="stylesheet" href="%s">', $url); |
||||||
|
} |
||||||
|
} |
@ -1,76 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
/* For licensing terms, see /license.txt */ |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace Chamilo\CoreBundle\Settings; |
|
||||||
|
|
||||||
use Sylius\Bundle\SettingsBundle\Schema\AbstractSettingsBuilder; |
|
||||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; |
|
||||||
use Symfony\Component\Finder\Finder; |
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; |
|
||||||
use Symfony\Component\Form\FormBuilderInterface; |
|
||||||
use Symfony\Contracts\Service\Attribute\Required; |
|
||||||
|
|
||||||
class StylesheetsSettingsSchema extends AbstractSettingsSchema |
|
||||||
{ |
|
||||||
private ParameterBagInterface $parameterBag; |
|
||||||
|
|
||||||
#[Required] |
|
||||||
public function setParameterBag(ParameterBagInterface $parameterBag): void |
|
||||||
{ |
|
||||||
$this->parameterBag = $parameterBag; |
|
||||||
} |
|
||||||
|
|
||||||
public function buildSettings(AbstractSettingsBuilder $builder): void |
|
||||||
{ |
|
||||||
$builder |
|
||||||
->setDefaults( |
|
||||||
[ |
|
||||||
'stylesheets' => 'chamilo', |
|
||||||
] |
|
||||||
) |
|
||||||
; |
|
||||||
$allowedTypes = [ |
|
||||||
'stylesheets' => ['string'], |
|
||||||
]; |
|
||||||
$this->setMultipleAllowedTypes($allowedTypes, $builder); |
|
||||||
} |
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder): void |
|
||||||
{ |
|
||||||
$builder |
|
||||||
->add('stylesheets', ChoiceType::class, [ |
|
||||||
'choices' => $this->getThemeChoices(), |
|
||||||
'label' => 'Select Stylesheet Theme', |
|
||||||
]) |
|
||||||
; |
|
||||||
|
|
||||||
$this->updateFormFieldsFromSettingsInfo($builder); |
|
||||||
} |
|
||||||
|
|
||||||
private function getThemeChoices(): array |
|
||||||
{ |
|
||||||
$projectDir = $this->parameterBag->get('kernel.project_dir'); |
|
||||||
$themesDirectory = $projectDir.'/assets/css/themes/'; |
|
||||||
|
|
||||||
$finder = new Finder(); |
|
||||||
$choices = []; |
|
||||||
|
|
||||||
$finder->directories()->in($themesDirectory)->depth('== 0'); |
|
||||||
if ($finder->hasResults()) { |
|
||||||
foreach ($finder as $folder) { |
|
||||||
$folderName = $folder->getRelativePathname(); |
|
||||||
$choices[$this->formatFolderName($folderName)] = $folderName; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $choices; |
|
||||||
} |
|
||||||
|
|
||||||
private function formatFolderName(string $name): string |
|
||||||
{ |
|
||||||
return ucwords(str_replace('_', ' ', $name)); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,45 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Chamilo\CoreBundle\State; |
||||||
|
|
||||||
|
use ApiPlatform\Metadata\Operation; |
||||||
|
use ApiPlatform\State\ProcessorInterface; |
||||||
|
use Chamilo\CoreBundle\Entity\AccessUrlRelColorTheme; |
||||||
|
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper; |
||||||
|
use Doctrine\ORM\EntityManagerInterface; |
||||||
|
|
||||||
|
final class AccessUrlRelColorThemeStateProcessor implements ProcessorInterface |
||||||
|
{ |
||||||
|
public function __construct( |
||||||
|
private readonly AccessUrlHelper $accessUrlHelper, |
||||||
|
private readonly EntityManagerInterface $entityManager, |
||||||
|
) {} |
||||||
|
|
||||||
|
public function process($data, Operation $operation, array $uriVariables = [], array $context = []): AccessUrlRelColorTheme |
||||||
|
{ |
||||||
|
assert($data instanceof AccessUrlRelColorTheme); |
||||||
|
|
||||||
|
$accessUrl = $this->accessUrlHelper->getCurrent(); |
||||||
|
$accessUrl->getActiveColorTheme()?->setActive(false); |
||||||
|
|
||||||
|
$accessUrlRelColorTheme = $accessUrl->getColorThemeByTheme($data->getColorTheme()); |
||||||
|
|
||||||
|
if ($accessUrlRelColorTheme) { |
||||||
|
$accessUrlRelColorTheme->setActive(true); |
||||||
|
} else { |
||||||
|
$data->setActive(true); |
||||||
|
|
||||||
|
$accessUrl->addColorTheme($data); |
||||||
|
|
||||||
|
$accessUrlRelColorTheme = $data; |
||||||
|
} |
||||||
|
|
||||||
|
$this->entityManager->flush(); |
||||||
|
|
||||||
|
return $accessUrlRelColorTheme; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Chamilo\CoreBundle\State; |
||||||
|
|
||||||
|
use ApiPlatform\Metadata\Operation; |
||||||
|
use ApiPlatform\State\ProviderInterface; |
||||||
|
use Chamilo\CoreBundle\Entity\AccessUrlRelColorTheme; |
||||||
|
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper; |
||||||
|
|
||||||
|
/** |
||||||
|
* @template-implements ProviderInterface<AccessUrlRelColorTheme> |
||||||
|
*/ |
||||||
|
class AccessUrlRelColorThemeStateProvider implements ProviderInterface |
||||||
|
{ |
||||||
|
public function __construct( |
||||||
|
private readonly AccessUrlHelper $accessUrlHelper, |
||||||
|
) {} |
||||||
|
|
||||||
|
/** |
||||||
|
* @inheritdoc |
||||||
|
*/ |
||||||
|
public function provide(Operation $operation, array $uriVariables = [], array $context = []) |
||||||
|
{ |
||||||
|
$colorThemes = $this->accessUrlHelper->getCurrent()->getColorThemes(); |
||||||
|
|
||||||
|
if (0 == $colorThemes->count()) { |
||||||
|
$colorThemes = $this->accessUrlHelper->getFirstAccessUrl()->getColorThemes(); |
||||||
|
} |
||||||
|
|
||||||
|
return $colorThemes; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Chamilo\Tests\CoreBundle\Controller; |
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
|
||||||
|
class ThemeControllerTest extends WebTestCase |
||||||
|
{ |
||||||
|
|
||||||
|
public function testValidAccess(): void |
||||||
|
{ |
||||||
|
$client = static::createClient(); |
||||||
|
|
||||||
|
$client->request('GET', '/themes/chamilo/colors.css'); |
||||||
|
|
||||||
|
$this->assertResponseIsSuccessful(); |
||||||
|
} |
||||||
|
|
||||||
|
public function testInvalidAccess(): void |
||||||
|
{ |
||||||
|
$client = static::createClient(); |
||||||
|
|
||||||
|
$client->request('GET', '/themes/chamilo/default.css'); |
||||||
|
|
||||||
|
$this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND); |
||||||
|
} |
||||||
|
|
||||||
|
public function testAccessToSystemFiles(): void |
||||||
|
{ |
||||||
|
$client = static::createClient(); |
||||||
|
$client->request('GET', '/themes/chamilo/../../../../../../etc/passwd'); |
||||||
|
|
||||||
|
$this->assertResponseStatusCodeSame(Response::HTTP_INTERNAL_SERVER_ERROR); |
||||||
|
|
||||||
|
|
||||||
|
$client->request('GET', 'themes/chamilo/../../../.env'); |
||||||
|
|
||||||
|
$this->assertResponseStatusCodeSame(Response::HTTP_INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
:root { |
||||||
|
--color-primary-base: 97 53 131; |
||||||
|
--color-primary-gradient: 36 77 103; |
||||||
|
--color-primary-button-text: 46 117 163; |
||||||
|
--color-primary-button-alternative-text: 255 255 255; |
||||||
|
|
||||||
|
--color-secondary-base: 243 126 47; |
||||||
|
--color-secondary-gradient: 224 100 16; |
||||||
|
--color-secondary-button-text: 255 255 255; |
||||||
|
|
||||||
|
--color-tertiary-base: 51 51 51; |
||||||
|
--color-tertiary-gradient: 0 0 0; |
||||||
|
--color-tertiary-button-text: 255 255 255; |
||||||
|
|
||||||
|
--color-success-base: 119 170 12; |
||||||
|
--color-success-gradient: 83 127 0; |
||||||
|
--color-success-button-text: 255 255 255; |
||||||
|
|
||||||
|
--color-info-base: 13 123 253; |
||||||
|
--color-info-gradient: 0 84 211; |
||||||
|
--color-info-button-text: 255 255 255; |
||||||
|
|
||||||
|
--color-warning-base: 245 206 1; |
||||||
|
--color-warning-gradient: 186 152 0; |
||||||
|
--color-warning-button-text: 0 0 0; |
||||||
|
|
||||||
|
--color-danger-base: 223 59 59; |
||||||
|
--color-danger-gradient: 180 0 21; |
||||||
|
--color-danger-button-text: 255 255 255; |
||||||
|
|
||||||
|
--color-form-base: 46 117 163; |
||||||
|
} |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |