Mapowanie kolorów scss

Zaczynamy od deklaracji kolorów które będą nam potrzebne w projekcie.

$colors: (
  grey: (
    base: #4d4e55,
    light: #c8c8ce
  ),
  yellow: (
    base: #ffae00
  ),
  green: (
    base: #00ff15
  )
);

Oczywiście nic nie stoi na przeszkodzie aby modyfikować naszą deklarację dodając następne nazwy kolorów light, lighter, lightest, dark, darker, darkest
Mniej więcej wyglądało by tak.

$colors: (
  grey: (
    base: #4d4e55,
    light: xxxx,
    lighter: xxxx,
    lightest: xxxx,
    dark: xxxx
    darker: xxxx
    darkest: xxxx
  ),
  yellow: (
    base: #ffae00
  ),
  green: (
    base: #00ff15
  )
);

Teraz czas na @mixin trochę przerażająco wygląda 😉

@mixin color-modifiers($attribute: 'color', $prefix: '-', $separator: '-', $base: 'base') {
  @each $name, $color in $colors {
    &#{$prefix}#{$name} {
      @each $tone, $hex in $color {
        &#{if($tone != $base, #{$separator}#{$tone}, '')} {
          #{$attribute}: $hex;
        }
      }
    }
  }
}

A użycie jest dość oczywiste 😉

.text {
  @include color-modifiers;
}

Wynik naszego kodu to zestaw czterech klas które zareklamowaliśmy powyżej.

.text-grey { color:#4d4e55 }
.text-grey-light { color:#c8c8ce }
.text-yellow { color:#ffae00 }
.text-green { color:#00ff15 }

create-react-app i scss

Aby umożliwić korzystanie z scss w create-react-app robię podstawowe rzeczy. Najpierw zerknij w package.json na razie jest minimalistycznie teraz z konsoli uruchamiam npm run eject

Potrzebnych nam są jeszcze trzy biblioteki node-sass, sass-loader oraz resolve-url-loader.
Ja używam yarna czyli uruchamiam

yarn add -D sass-loader node-sass resolve-url-loader

lub dla tych co używają NPM

npm i -D sass-loader node-sass resolve-url-loader

Najpierw zajmiemy się wersją deweloperską znajduje się w folderze config -> webpack.config.dev.js
Kod wstawiam między
test: /\.(js|jsx|mjs)$/ a między test: /\.css$/

{
    test: /\.scss$/,
        include: [paths.appSrc, paths.appNodeModules],
            use: [
                {
                    loader: require.resolve('style-loader'),
                    options: {
                        sourceMap: true
                    }
                },
                {
                    loader: require.resolve('css-loader'),
                    options: {
                        importLoaders: 1,
                        modules: true,
                        localIdentName: '[local]'
                    },
                },
                require.resolve('resolve-url-loader'),
                {
                    loader: require.resolve('sass-loader'),
                    options: {
                        sourceMap: true
                    }
                }
            ]
},

a wersji produkcyjnej która również znajduje się w folderze config -> webpack.config.prod.js
Również w tym samym miejscu umieszczamy poniższy kod miedzy test: /\.(js|jsx|mjs)$/ a test: /\.css$/

{
    test: /\.scss$/,
        include: [paths.appSrc, paths.appNodeModules],
            use: [
                {
                    loader: require.resolve('style-loader'),
                    options: {
                        sourceMap: true
                    }
                },
                {
                    loader: require.resolve('css-loader'),
                    options: {
                        importLoaders: 1,
                        modules: true,
                        localIdentName: '[sha1:hash:hex:4]',
                    },
                },
                require.resolve('resolve-url-loader'),
                {
                    loader: require.resolve('sass-loader'),
                    options: {
                        sourceMap: true
                    }
                }
            ]
},

Różnica między dev jest localIdentName: ‚[local]’ a prod localIdentName: ‚[sha1:hash:hex:4]’ w dev pokazuję normalne klasy ‚home’ czy ‚container’ a w prod te klasy są zmienione np. na coś takiego ‚ea27’ 😉 Oczywiście

I tyle teraz uruchamiamy yarn start lub npm start

Mini sass grid

.row {
    display: flex;
    flex-wrap: wrap;
}

$columns: 12;

@for $i from 1 through $columns {
    .col-#{$i} {
        flex: 0 0 100% / $columns * $i;
    }
    .col-offset-#{$i} {
        margin-left: 100% / $columns * $i;
    }
}

// Responsive Breakpoints
[class^="col-"] {
    // Tablets
    @media (max-width: 768px) {
        flex-basis: 50%;
    }
    // Mobiles
    @media (max-width: 480px) {
        flex-basis: 100%;
    }
}

Docker + wordpress + phpmyadmin wersja Windows

Nie opiszę tutaj wszystkiego związanego z dockerem bo jestem świerzynka w tym temacie oraz jest tego tyle że po prostu nie da się tego opisać bez przepisywania dokumentacji 😉
Będzie to tylko mały wycinek tego co jest mi potrzebne do frontentu z przykładem użycia WordPress, MySQL oraz phpMyAdmin na Windowsie.

Do czego można by użyć Dockera?

Powiedźmy że chcemy stworzyć aplikację składającą się właśnie z WordPress + MySQL + phpMyAdmin aby przygotować nowy theme czy plugin.
Wszystko to musimy zainstalować osobno na komputerze do tego PHP + apache2 lub użyć Xampp’a, ale co jeśli musielibyśmy użyć kilku wersji PHP 5.5 czy 5.6 i do tego MySQL również w kilku wersjach.

Normalnie czekało by nas bardzo długie konfigurowanie każdej instancji, odinstalowanie elementów i instalacja nowych oraz ich konfiguracja czyli jednym słowem koszmar.

W takich przypadkach pomocny jest właśnie Docker. Wystarczy wybrać kontener z wersją aplikacji i już. Można nawet uruchomić wiele kontenerów równolegle, dzięki czemu możemy testować aplikację w kilku rożnych wersjach w tym samym czasie!
Najważniejsze przynajmniej dla mnie jest to że nie zaśmiecamy sobie środowiska zbędnymi instalacjami, a jak wiemy w tym względzie Windows jest dość ułomnym środowiskiem i można go łatwo „zepsuć”.

Teraz trochę definicji.

Docker jest narzędziem , które pozwala umieścić program oraz jego zależności w przenośnym wirtualnym kontenerze. Umożliwia utworzenie kontenerów zawierających pojedyncze aplikacje ale także łączyć je w większe środowiska.

Ok zaczynamy zabawę.

Najpierw należy zainstalować Dockera na komputerze cały proces jest opisany na zerknij tutaj
W dalszej części będziemy korzystać z narzędzia Docker Compose ono jest dla wersji MAC jak i Windows jest zaszyte w instalacji Docker zaś linuxowcy muszą je osobno doinstalować.

Otwieramy konsole i wpisujemy

docker run hello-world

Jeżeli wszystko przebiegło poprawnie powinniśmy zobaczyć w konsoli to co poniżej.

Sprawdźmy jakie mamy obrazy, powinien być jeden hello-world, aby to sprawdzić piszemy

docker images

i dostajemy

Może co poważniejszego zainstalujmy na przykład nginx

docker container run -it -p 80:80 nginx

Kilka chwil i mamy uruchomioną instancję nginx, otwieramy przeglądarkę i http://localhost/index.html i mamy powitalną stronę

Teraz wyobraźcie sobie ile czasu by to zajęło w konwencjonalnym podejściu instalacja tego pod Windosem 🙂

Kilka przydatnych komend poniżej.

  • docker – wyświetla wszystkie podstawowe komendy
  • docker help – podstawowa komenda dzięki której można sprawdzić składnie oraz wyszukać inne komendy
  • docker pull [nazwaObrazu] – pobranie obrazu
  • docker search [imageName:imageTag] – komenda wyszukuje obrazy o podanej nazwie i tagu
  • docker run [args] [imageName:imageTag/imageId] – stworzenie oraz uruchomienie kontenera z obrazu
  • -it – tryb interaktywny (możemy np: korzystać z bash’a)
  • -p – protokół mapuje wystawiony port naszego kontenera na port naszego localhost
  • -d – tryb deamon, działa na zasadzie usługi
  • -expose – pozwala nam wystawić post naszego kontenera na zewnątrz
  • docker ps – komenda pokazuje nam działające kontenery jeżeli chcemy zobaczyć również te niedziałające docker ps -a
  • docker kill/stop [containerName/containerId] – komenda służy do wyłączenia działającego kontenera. Zazwyczaj korzysta się z containerId z 3 pierwszych znaków
  • docker docker rename [containerOldName] [containerNewName] – komenda służy do zmiany nazwy kontenera
  • docker rm [containerName/containerId] – usunięcie kontenera z naszego komputera o zadanej nazwie lub id
  • docker rmi [imageName:imageTag/imageId] – usuniecie z naszego komputera obrazu o podanej nazwie i tagu lub id

To tylko ułamek wszystkich flag więcej można znaleźć tutaj.

Przejdźmy do konkretów.

A mianowicie chce uruchomić tak jak na wstępie pisałem WordPress + MySQL + phpMyaAmin i swój własny theme i tutaj przyda nam się Docker Compose.
Najpierw usuńmy pozostałości po naszych poprzednich próbach. Zobaczmy co jest zainstalowane najpierw

docker images -a

później

docker rm containerId

– czyli pierwsze 3 znaki kontenera i enter.

Zaczynamy od stworzenia folderu

mkdir docker-setup

następnie

cd docker-setup

i uruchamiam visual studio code przez komendę code .

Do vsc instaluje plugin Docker poniżej


plugin pokazuję kontenery images oraz Docker Hub wystarczy kliknąć na wielorybka, trochę upraszcza nam pracę mniej pisania w konsoli 😉

Na początku tworzę w docker-setup .dockerignore praktycznie coś na styl gita

Następnie tworzę plik docker-compose.yml w nim będzie działać się cała magia 🙂

  • version – oznacza wersję Docker Compose
  • services – sekcja, w której definiujemy serwisy
  • mysql:5.7 – nazwa własna usługi
  • image – wskazujemy z jakiego obrazu ma być zbudowany kontener
  • build – wskazujemy ścieżkę do pliku Dockerfile naszego serwisu – w projekcie nie używamy
  • ports – definiujemy porty, które będą mapowane z konteneru
  • links – zależność pomiędzy kontenerami, db oraz WordPress
  • environment – zmienna środowiskowa dla danego kontenera

Aby uruchomić Docker Compose w naszym terminalu używamy komendy

docerk-compose up

w miejscu w którym mamy docker-compose.yml

Za pierwszym razem wszystkie zależności które są w yml są zaciągane – nasza baza danych jak i WordPress.
Poniżej screen z vsc oraz z chrome w którym mamy WordPress w wersji do zainstalowania 🙂

Dodajmy do tego całego naszego środowiska również phpMyAdmin

phpmyadmin:
 depends_on:
  - db
 image: phpmyadmin/phpmyadmin
 ports:
  - 8080:80
 environment:
  MYSQL_ROOT_PASSOWRD: test

Musimy najpierw zastopować wszystkie kontenery czy to przez „ctrl+c” lub z innej konsoli „docker-compose down” albo z vsc prawy przycisk na poszczególny kontener i Remove Container jak kto woli. Ja używam docker-compose down jest najpewniejszym sposobem zamknięcia uruchomionych kontenerów.

Po dodaniu phpMyAdmin uruchamiamy ponownie „docker-compose up”

Wszystko działa szybciej bo pobierany jest tylko obraz phpMyAdmin, aby sprawdzić czy działa wchodzimy na to co ostawiliśmy mianowicie http://localhost:8080
Podajemy „Użytkownika root Hasło test” tak jak podaliśmy w docker-compose.yml
I naszym oczom ukazuje się phpMyAdmin z utworzoną baza wp

Wracamy do okna http://localhost/wp-admin/install.php tam instalujemy jak zawsze i wypełniamy danymi ale ZARAZ ZARAZ!!! jeśli wyłączymy wszystkie kontenery to co dodaliśmy do WordPress a mianowicie strony, artykuły wszystko to nam zniknie!!! Nie no tak to nie możne być!

Dodajemy własne artykuły wszystko co jest nam potrzebne w WordPressa i za pomocą phpMyAdmin eksportujemy bazę do pliku db.sql (nazwa oczywiście może być inna) nie musimy tego robić możemy pobrać jakąś inną bazę danych z innego WordPress jeżeli takową posiadamy i wystarczy to przygotowania naszego theme.
Dump bazy umieszczam w folderze „data/db.sql” w naszym projekcie oraz dodaje odpowiedni wpis w docker-compose.yml

db:
 image: mysql:5.7
 volumes:
  - ./data:/docker-entrypoint-initdb.d
 restart: always
 ...

volumes pobiera nam db.sql za każdym razem gdy uruchamiamy „docker-compose up” i mamy z głowy uzupełnianie danych w bazie, możemy wejść na http://localhost i zobaczyć naszego WordPress

Dodajemy własne theme.

Teraz druga cześć chcemy dodać własny theme, ponownie stopujemy docker-compose down w głównym folderze naszego projektu dodaje folder wp-content/themes/newTemplate z plikami odpowiedzialnymi za theme.
Teraz aby to zostało zaciągnięte i widoczne w WordPressie należy dodać values tym razem do wpisu wordpress.

Ponownie docker-compose up, teraz możemy pracować na theme, aby zobaczyć zmiany które zrobiliśmy w theme wystarczy odświeżyć WordPress F5 w przeglądarce. Nie trzeba stopować kontenerów.

Źródła znajdują się pod tym adresem

Gulp workflow: minimalizacja zdjęć i konwersja na webp

Kiedyś w zamierzchłych czasach używałem perla + lib ImageMagick do konwersji zdjęć, ale mamy XXI wiek i Node 😉
Od jakiegoś czasu zmienił się mój workflow dotyczący obróbki zdjęć na potrzeby stron internetowych.
Do tej pory używałem gulp-image, gulp-imagemin i wiele innych, ale zawsze coś było nie tak, zawsze czegoś brakowało.
Ostatnio używam takiego zapisu jak poniżej

<picture>
    <source class="img-responsive" srcset="./images/thumbnail/IMG_5600.webp" type="image/webp">
    <source class="img-responsive" srcset="./images/thumbnail/IMG_5600.jpg" type="image/jpeg">
    <img class="img-responsive" src="./images/thumbnail/IMG_5600.jpg" alt="">
</picture>

i potrzebowałem również webp, no i co następna biblioteka gulp-webp jest potrzebna 🙁
Mały research i okazało się że jest biblioteka sharp, która bardzo dobrze zastępuje ImageMagick więcej na tej stronie. Biblioteka ta jest w stanie zastąpić wszystko co do tej poru używałem.

Obecnie korzystam z pluginu gulp-responsive który pod spodem używa tejże biblioteki sharp.
Zmiana rozmiaru obrazu za pomocą tej biblioteki jest zazwyczaj 4x-5x szybsza niż w przypadku najszybszych ustawień ImageMagick i GraphicsMagick, a więc czemu jej nie używać. Oczywiście ta bibliotek nie tylko do zmniejsza grafiki ale również przycina, zmieniania formatu, zapisu do grayscale czyli szarości i wiele innych użytecznych rzeczy potrafi 🙂

Dobra zaczynamy cały proces.
Oczywiście musimy mieć zainstalowane node w naszym środowisku ale to nie temat na ten artykuł.

Na początek tworzymy package.json

npm init -y

Instalujemy wszystkie potrzebne biblioteki

npm i -D gulp gulp-responsive gulp-load-plugins

Tworzymy folder ze zdjęciami „sources” następnie gulpfile.js

var gulp = require('gulp');
var $ = require('gulp-load-plugins')();

gulp.task('images', function () {
    return gulp.src(['sources/**/*.{jpg,png}'])
        .pipe($.responsive({
            '*.jpg': [{
                width: 1200,
                height: 800
            }, {
                // Konwertujemy jpg do formatu webp
                width: 1200,
                height: 800,
                rename: {
                    extname: '.webp'
                }
            }]
        }, {
                // globalne ustawienia
                max: true,
                quality: 65,
                progressive: true,
                withMetadata: false,
                errorOnEnlargement: true
            }))
        .pipe(gulp.dest('dist'));
});

Oczywiście aby ten wynalazek uruchomić należy tak jak wszystkie taski w gulpie czyli:

gulp images

Ustawienia globalne:
max parametr potrzebny gdy zdjęcie jest zrobione w pionie – trzyma proporcje
quality wiadomo jakość dla grafik jpg jak i webp
progressive, jpg ładuje się od góry do dołu linia po linii, progresywny ładuje się całościowo w „kiepskiej” jakości i jest stopniowo wyostrzany
withMetadata usuwane są wszystkie metadane z pliku
errorOnEnlargement pokazuje błędy jeżeli takowe się pojawią

Biblioteka jest bardzo szybka 137 zdjęć (ponad 550MB) w rozdzielczości 2700×1800 zajęło tej bibliotece 32s z ustawieniami globalnymi jak powyżej bez webp. Oczywiście szybkość zależy od hardwaru jaki posiadamy.

Więcej można przeczytać na githubie opisane są tam bardziej skomplikowane użycia tej biblioteki jak dostępne opcje konfiguracyjne.

Axios oraz php wysłanie formularza.

Za pomocą axios wysyłam formularz.
Najpierw zbieram dane z formularzy, ustawiam content-type i wysyłam postem – axios.post

let data = {
    'imie-i-nazwisko': document.getElementById('name').value,
    'data-wydarzenia': document.getElementById('date').value
};

let config = {
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
    }
}
axios.post('mail.php', data, config)
    .then(response => {
        console.log(response);
    })
    .catch(error => {
        console.log(error);
    });

Następna rzecz okazała się dość problematyczna, a mianowicie sparsowanie jsona do zmiennej w php.
Całe clou to file_get_contents(‚php://input’)

$_POST = json_decode(file_get_contents('php://input'), true);

if($_POST) {
    $imie_i_nazwisko = trim($_POST['imie-i-nazwisko']);
    ...
}

DOMContentLoaded async

Niestety jeżeli używamy async DOMContentLoaded nam nie zadziała.

<script type="text/javascript" src="./index.6da4.js" async>

Trochę mi zajęło zanim odkryłem ten problem. Lokalnie wszystko działało bo w webpaku w wersji developerskiej nie używam pluginu który dodaje async do script. Dopiero wersja produkcyjna posiada tą zmianę.

document.addEventListener("DOMContentLoaded", function() {
   const contact = new FormValidate('form');
   contact.init();
});

Przeszukanie sieci doprowadziło mnie do tego rozwiązania, działa nawet w IE9.

document.addEventListener('readystatechange', () => {
    if (document.readyState == "complete") {
        const contact = new FormValidate('form');
        contact.init();
    }
});

addEventListener wiele zdarzeń

Zamiast wywoływać dwa razy tą samą funkcję.

document.addEventListener("click", taSamaFunkcja);
document.addEventListener("touchstart", taSamaFunkcja);

Możemy to zrobić tak jak poniżej

["change", "keyup", "click", "touchstart", "input", "..."].forEach(function(event) {
  document.querySelectorAll('.element').addEventListener(event, function() {
    // i tutaj co potrzeba
  }, false);
});

Usunięcie plików i folderów z githuba po ich dodaniu.

Dodaliśmy pliki do githuba ale coś się zmieniło i chcielibyśmy teraz je usunąć.
Dodaliśmy je to .gitignore ale to niestety tak nie zadziała.

Oczywiście należy dodać pliki czy foldery najpierw do .gitignore
Później odpalamy konsolę i wpisujemy poniższe komendy.

git rm -r --cached .
git add .
git commit -m 'Removed all files that are in the .gitignore'
git push origin master

I tym sposobem czyścimy niepotrzebne pliki z repozytorium na githubie.

Gulp ES6 z Babel

Do gulp instalujemy:

npm install babel-core babel-preset-es2015 --save-dev

W głównym folderze tworzymy plik:

.babelrc

W nim dodajemy zapis:

{
  "presets": [
    "es2015"
  ]
}

Aby zadziałał nam baabel z gulp należy zmodyfikować nazwę gulpfile.js na gulpfile.babel.js
Ja na wszelki wypadek duplikuję plik i zmieniam nazwę tak na wszelki wypadek i później dokonuję odpowiednich zmian w gulpfile.babel.js

'use strict';

import gulp from 'gulp';
import sass from 'gulp-sass';
import autoprefixer from 'gulp-autoprefixer';
import sourcemaps from 'gulp-sourcemaps';

const dir = {
  src: 'src',
  dest: 'build'
};

const sassPaths = {
  src: `${dir.src}/style.scss`,
  dest: `${dir.dest}/style/`
};

gulp.task('style', () => {
  return gulp.src(sassPaths.src)
    .pipe(sourcemaps.init())
    .pipe(sass.sync().on('error', plugins.sass.logError))
    .pipe(autoprefixer())
    .pipe(gulp.dest(sassPaths.dest));
});

...