Merge commit 'd12d3b2cecbfb5a85bf219fa146a02b523dd6352'

* commit 'd12d3b2cecbfb5a85bf219fa146a02b523dd6352':
  systemctl removed ".service"
  comments about Certbot renewal fixes #79
  nginx -> NGINX
  style fixes
  refactored command with steps
  site group, preset group
  moved github to header
  moved loader to header
  build:scss:watch
  application/rss+xml fix fixes #89
  node-sass
  site selector refactor

# Conflicts:
#	package.json
pull/85/head
Bálint Szekeres 2019-05-19 18:20:18 +02:00
commit 54d66e9e45
14 changed files with 1499 additions and 1115 deletions

954
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,14 +22,14 @@
"angular-tooltips": "^1.2.2",
"autoprefixer": "^8.6.4",
"bootstrap": "^4.2.1",
"eslint": "^5.9.0",
"eslint": "^5.16.0",
"highlight.js": "^9.12.0",
"js-base64": "^2.5.1",
"node-sass": "^4.11.0",
"node-sass": "^4.12.0",
"postcss-cli": "^5.0.1"
},
"devDependencies": {
"cypress": "^3.1.2",
"cypress": "^3.2.0",
"http-server": "^0.11.1",
"start-server-and-test": "^1.5.0"
},
@ -39,6 +39,7 @@
"build:prod": "npm run build:scss:prod && npm run autoprefixer",
"build:scss": "node-sass --source-map=public/assets/css/app.min.css.map resources/scss/app.scss public/assets/css/app.min.css",
"build:scss:prod": "node-sass --output-style=compressed resources/scss/app.scss public/assets/css/app.min.css",
"build:scss:watch": "npm run build:scss -- --watch",
"autoprefixer": "postcss public/assets/css/app.min.css --use autoprefixer --no-map --replace --verbose",
"lint": "eslint public/assets/js/app.js",
"lint:fix": "npm run lint -- --fix",

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check-circle"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>

After

Width:  |  Height:  |  Size: 328 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-download-cloud"><polyline points="8 17 12 21 16 17"></polyline><line x1="12" y1="12" x2="12" y2="21"></line><path d="M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29"></path></svg>

After

Width:  |  Height:  |  Size: 387 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-lock"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>

After

Width:  |  Height:  |  Size: 321 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-terminal"><polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line></svg>

After

Width:  |  Height:  |  Size: 310 B

View File

@ -205,7 +205,7 @@
}
for (var i in siteTabs) {
$scope.siteChanges[$scope.site][siteTabs[i]] = $window.document.querySelectorAll('section.tabs .tab-content.site-tab-content .tab-' + siteTabs[i] + ' .form-group:not(.disabled) .input-changed').length;
$scope.siteChanges[$scope.site][siteTabs[i]] = $window.document.querySelectorAll('section.tabs .tab-content.site-content .tab-' + siteTabs[i] + ' .form-group:not(.disabled) .input-changed').length;
}
for (var j in commonTabs) {
@ -385,7 +385,9 @@
$scope.clipboardCopy = undefined;
$scope.gzipTypes = 'text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml';
$scope.activeStep = 'download';
$scope.gzipTypes = 'text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml';
$scope.extensions = {
assets: 'css(\\.map)?|js(\\.map)?',
@ -521,6 +523,10 @@
});
};
$scope.setActiveStep = function(step) {
$scope.activeStep = step;
};
$scope.getSiteChanges = function(site) {
if ($scope.siteChanges[site] === undefined) {
return undefined;
@ -1027,6 +1033,7 @@
// www
$scope.data.sites[site].domain = $scope.data.sites[site].domain.replace(/^https?:\/\//, '');
$scope.data.sites[site].domain = $scope.data.sites[site].domain.replace(/\/.*$/, '');
$scope.data.sites[site].domain = $scope.data.sites[site].domain.replace(' ', '');
if ($scope.data.sites[site].domain.match(/^www\./)) {
$scope.data.sites[site].domain = $scope.data.sites[site].domain.replace(/^www./, '');

File diff suppressed because it is too large Load Diff

View File

@ -1,65 +1,70 @@
<!-- ✔ symlink --><span ng-if="isSymlink()"><!--
<span class="step" ng-if="activeStep === 'download'"><!--
--><span class="hljs-comment"># <strong>Virtual host</strong>: create symbolic link{{ getDomains().length > 1 ? 's' : '' }}</span>
<span class="hljs-section">ln</span> <span class="hljs-attribute">-s</span> <span ng-repeat="(_site, _domain) in getDomains() track by $index">/etc/nginx/sites-available/{{ _domain }}.conf </span>/etc/nginx/sites-enabled</span><!--
step: download
--><span class="hljs-comment"><span class="counter">.</span> Download generated config:</span> <strong><a href="#" ng-click="downloadZip()">nginxconfig.io-{{ getDomains().join(',') }}.zip</a></strong>
<span class="hljs-comment"><span class="counter">.</span> Upload to server's <strong>/etc/nginx/</strong> directory</span>
<span class="hljs-comment"><span class="counter">.</span> Backup current configuration:</span>
<span class="hljs-section">tar</span> <span class="hljs-attribute">-czvf</span> nginx_$(date +'%F_%H-%M-%S').tar.gz nginx.conf sites-available/ sites-enabled/ nginxconfig.io/
<span class="hljs-comment"><span class="counter">.</span> Unzip the uploaded archive:</span>
<span class="hljs-section">cd</span> /etc/nginx
<span class="hljs-section">unzip</span> <span class="hljs-attribute">-o</span> nginxconfig.io-{{ getDomains().join(',') }}.zip <span class="hljs-comment">(you probably need to install <strong>unzip</strong>:</span> <span class="hljs-section"></span><strong>sudo</strong> <span class="hljs-section">apt-get</span> <span class="hljs-attribute">install</span> unzip<span class="hljs-comment">)</span></span><!--
✔ symlink || ✔ HTTPS --><span ng-if="isSymlink() && (isSSLDHRequired() || hasCertLetsEncrypt())">
--><span class="step" ng-if="activeStep === 'ssl'"><!--
step: ssl
--><!--
✔ SSL DH required --><span ng-if="isSSLDHRequired()"><span class="hljs-comment"><span class="counter">.</span> Generate Diffie-Hellman keys:</span>
<span class="hljs-section">openssl</span> <span class="hljs-attribute">dhparam -dsaparam</span> <span class="hljs-attribute">-out</span> /etc/nginx/dhparam.pem <span class="hljs-number">{{ isSSLProfileLegacy() ? 1024 : 2048 }}</span>
</span><!--
✔ SSL DH required --><span ng-if="isSSLDHRequired()"><!--
--><span class="hljs-comment"># <strong>HTTPS</strong>: create Diffie-Hellman keys</span>
<span class="hljs-section">openssl dhparam</span> <span class="hljs-attribute">-dsaparam</span> <span class="hljs-attribute">-out</span> /etc/nginx/dhparam.pem <span class="hljs-number">{{ isSSLProfileLegacy() ? 1024 : 2048 }}</span><!--
--><span ng-if="hasCertLetsEncrypt()">
</span></span><!--
✔ Let's Encrypt --><span ng-if="hasCertLetsEncrypt()"><!--
--><span class="hljs-comment"># <strong>HTTPS - certbot</strong> (before first run): create ACME-challenge common directory</span>
<span class="hljs-section">mkdir</span> <span class="hljs-attribute">-p</span> /var/www/_letsencrypt && <span class="hljs-section">chown</span> <span class="hljs-attribute">{{ data.user }}</span> /var/www/_letsencrypt
<!--
✔ Let's Encrypt
--><span ng-if="hasCertLetsEncrypt()"><span class="hljs-comment"><span class="counter">.</span> Create a common ACME-challenge directory (for Let's Encrypt):</span>
<span class="hljs-section">mkdir</span> <span class="hljs-attribute">-p</span> /var/www/_letsencrypt
<span class="hljs-section">chown</span> <span class="hljs-attribute">{{ data.user }}</span> /var/www/_letsencrypt</span></span><!--
-->
<span class="hljs-comment"># <strong>HTTPS - certbot</strong> (before first run): disable SSL directives</span>
<span class="hljs-section">sed</span> <!--
--><span class="hljs-attribute">-i</span> <!--
--><span class="hljs-attribute">-r</span> <!--
-->'s/(listen .*443)/\1;#/g; s/(ssl_(certificate|certificate_key|trusted_certificate) )/#;#\1/g'<span ng-if="isUnified()"><!--
--><span class="step" ng-if="activeStep === 'certbot'"><!--
step: certbot
--><span class="hljs-comment"><span class="counter">.</span> Comment out SSL related directives in configuration:</span>
<span class="hljs-section">sed</span> <span class="hljs-attribute">-i -r</span> 's/(listen .*443)/\1;#/g; s/(ssl_(certificate|certificate_key|trusted_certificate) )/#;#\1/g'<span ng-if="isUnified()"><!--
--> /etc/nginx/nginx.conf</span><span ng-if="isModularized()"><span ng-repeat="(_site, _domain) in getDomains() track by $index" ng-if="isCertLetsEncrypt(_site)"><!--
--> /etc/nginx/sites-{{ isSymlink() ? 'available' : 'enabled' }}/{{ _domain }}.conf</span></span>
<!--
-->
<span class="hljs-comment"># <strong>HTTPS - certbot</strong>: obtain certificates</span><!--
<span class="hljs-comment"><span class="counter">.</span> Configure <strong>Certbot</strong> to reload <strong>NGINX</strong> on success:</span>
<span class="hljs-section">echo</span> <span class="hljs-attribute">-e</span> '#!/bin/bash\nnginx -t && systemctl reload nginx' | <strong>sudo</strong> <span class="hljs-section">tee</span> /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh
<strong>sudo</strong> <span class="hljs-section">chmod</span> <span class="hljs-attribute">a+x</span> /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh
<span class="hljs-comment"><span class="counter">.</span> Obtain certificate{{ getDomains().length > 1 ? 's' : '' }}:</span><!--
--><span ng-repeat="(_site, _domain) in getDomains() track by $index" ng-if="isCertLetsEncrypt(_site)">
<span class="hljs-section">certbot certonly</span> <!--
<span class="hljs-section">certbot</span> <!--
--><span class="hljs-attribute">certonly</span> <!--
--><span class="hljs-attribute">--webroot</span> <!--
--><span ng-if="isNonWWW(_site) || isRedirect(_site)"><span class="hljs-attribute" tooltips tooltip-template="--domain">-d</span> {{ _domain }} </span><!--
--><span ng-if="isWWW(_site) || isRedirect(_site)"><span class="hljs-attribute" tooltips tooltip-template="--domain">-d</span> www.{{ _domain }} </span><!--
--><span ng-if="isCDN(_site)"><span class="hljs-attribute" tooltips tooltip-template="--domain">-d</span> cdn.{{ _domain }} </span><!--
--><span ng-if="isNonWWW(_site) || isRedirect(_site)"><span class="hljs-attribute" tooltips tooltip-template="--domain" tooltip-side="top">-d</span> {{ _domain }} </span><!--
--><span ng-if="isWWW(_site) || isRedirect(_site)"><span class="hljs-attribute" tooltips tooltip-template="--domain" tooltip-side="top">-d</span> www.{{ _domain }} </span><!--
--><span ng-if="isCDN(_site)"><span class="hljs-attribute" tooltips tooltip-template="--domain" tooltip-side="top">-d</span> cdn.{{ _domain }} </span><!--
--><span class="hljs-attribute">--email</span> {{ data.sites[_site].email ? data.sites[_site].email : 'info@' + _domain }} <!--
--><span class="hljs-attribute" tooltips tooltip-template="--webroot-path">-w</span> /var/www/_letsencrypt <!--
--><span class="hljs-attribute" tooltips tooltip-template="--non-interactive">-n</span> <!--
--><span class="hljs-attribute" tooltips tooltip-template="--webroot-path" tooltip-side="top">-w</span> /var/www/_letsencrypt <!--
--><span class="hljs-attribute" tooltips tooltip-template="--non-interactive" tooltip-side="top">-n</span> <!--
--><span class="hljs-attribute">--agree-tos</span> <!--
--><span class="hljs-attribute">--force-renewal</span></span>
<!--
-->
<span class="hljs-comment"># <strong>HTTPS - certbot</strong> (after first run): enable SSL directives</span>
<span class="hljs-section">sed</span> <!--
--><span class="hljs-attribute">-i</span> <!--
--><span class="hljs-attribute">-r</span> <!--
-->'s/#?;#//g'<span ng-if="isUnified()"><!--
<span class="hljs-comment"><span class="counter">.</span> Uncomment SSL related directives in configuration:</span>
<span class="hljs-section">sed</span> <span class="hljs-attribute">-i -r</span> 's/#?;#//g'<span ng-if="isUnified()"><!--
--> /etc/nginx/nginx.conf</span><span ng-if="isModularized()"><span ng-repeat="(_site, _domain) in getDomains() track by $index" ng-if="isCertLetsEncrypt(_site)"><!--
--> /etc/nginx/sites-{{ isSymlink() ? 'available' : 'enabled' }}/{{ _domain }}.conf</span></span></span>
--> /etc/nginx/sites-{{ isSymlink() ? 'available' : 'enabled' }}/{{ _domain }}.conf</span></span>
<span class="hljs-comment"><span class="counter">.</span> Schedule renewing:
You're ready, Certbot will renew any certificates that expire in less than 30 days</span></span><!--
--><span class="step" ng-if="activeStep === 'live'"><!--
step: live
--><span class="hljs-comment"><span class="counter">.</span> Reload <strong>NGINX</strong></span>
<strong>sudo</strong> <span class="hljs-section">nginx</span> <span class="hljs-attribute">-t</span> && <strong>sudo</strong> <span class="hljs-section">systemctl</span> <span class="hljs-attribute">reload</span> nginx</span>

View File

@ -1,44 +1,195 @@
@keyframes sk-bounce {
0%, 100% {
transform: scale(0.0);
} 50% {
transform: scale(1.0);
}
}
header {
background-color: #000;
padding: 0.5rem 0 0.25rem;
padding: 0.5rem 0;
text-align: center;
margin-bottom: 0.5rem;
color: #fff;
font-size: 2.2rem;
.logo {
height: 3rem;
margin: 0.5rem 0 0.3rem;
.container {
position: relative;
}
.presets {
margin-bottom: 0.3rem;
line-height: 1.8rem;
.loader {
position: absolute;
top: 5px;
left: 50%;
pointer-events: none;
margin-left: -230px;
.btn-outline-light {
border-color: #999;
.spinner {
width: 50px;
height: 50px;
position: relative;
&.active,
&:hover {
background-color: #fff;
border-color: #fff;
svg {
path {
fill: #000;
}
}
.double-bounce1,
.double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #999;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;
animation: sk-bounce 2.0s infinite ease-in-out;
}
svg {
height: 1.1rem;
vertical-align: text-bottom;
path {
fill: #ddd;
transition: fill 0.15s ease-in-out;
}
.double-bounce2 {
animation-delay: -1.0s;
}
}
}
.logo {
height: 3rem;
margin-bottom: 0.5rem;
}
.github {
@include media-breakpoint-up(lg) {
position: absolute;
right: 15px;
top: 10px;
}
.github-link {
text-align: center;
font-size: 1rem;
margin-bottom: 0.5rem;
a {
color: #fff;
}
small {
font-size: 90%;
}
svg {
height: 1.2rem;
vertical-align: -4px;
g {
fill: #fff;
}
}
}
.github-star {
width: 144px;
height: 30px;
display: block;
border: 0;
overflow: hidden;
margin: 0 auto 0.25rem;
@include media-breakpoint-up(lg) {
margin-left: auto;
}
}
}
.sites {
@include media-breakpoint-up(lg) {
margin-top: -1rem;
}
}
.sites,
.presets {
font-size: 0;
text-align: center;
@include media-breakpoint-up(lg) {
text-align: left;
}
}
.sites .title,
.presets .title {
color: #eee;
font-size: 0.9rem;
line-height: 1.9rem;
font-style: italic;
vertical-align: top;
margin-right: 4px;
text-align: center;
@include media-breakpoint-up(lg) {
display: inline-block;
width: 60px;
}
}
.sites .group,
.presets .group {
@include media-breakpoint-up(lg) {
display: inline-block;
vertical-align: top;
}
}
.sites .group {
@include media-breakpoint-up(lg) {
width: calc(100% - 290px);
}
}
.presets .group {
@include media-breakpoint-up(lg) {
width: calc(100% - 64px);
}
}
.btn-outline-light {
box-shadow: none !important;
border-color: #999;
margin: 0 3px 4px;
&.active,
&:hover {
background-color: #fff;
border-color: #fff;
svg {
path {
fill: #000;
}
}
}
svg {
height: 1.1rem;
vertical-align: text-bottom;
path {
fill: #ddd;
transition: fill 0.15s ease-in-out;
}
}
small {
margin-left: 0.2rem;
font-weight: 700;
vertical-align: 1px;
}
.close {
float: none;
vertical-align: -2px;
color: #666;
line-height: 0;
margin-right: -0.3rem;
font-size: 1.3rem;
}
}
}

View File

@ -1,15 +1,8 @@
@keyframes sk-bounce {
0%, 100% {
transform: scale(0.0);
} 50% {
transform: scale(1.0);
}
}
section.tabs {
margin-bottom: 1rem;
.sites {
position: relative;
margin-bottom: 1rem;
}
@ -35,25 +28,13 @@ section.tabs {
}
}
&.nav-site {
.nav-item {
.nav-link {
&.changed {
font-weight: normal;
}
&.active {
font-weight: 700;
}
}
}
}
&.nav-site-tab {
.nav-item {
.nav-link {
&:hover {
background-color: #f8f8f8;
@include media-breakpoint-up(xl) {
background-color: #f8f8f8;
}
}
&.active {
@ -149,7 +130,7 @@ section.tabs {
}
}
.tab-content {
padding: 0.5rem 0.75rem;
padding: 0.5rem 0.75rem 0.25rem;
border-left: 1px solid #dee2e6;
border-right: 1px solid #dee2e6;
border-bottom: 1px solid #dee2e6;
@ -157,10 +138,6 @@ section.tabs {
border-bottom-left-radius: 0.25rem;
&.site-content {
padding-bottom: 0.75rem;
}
&.site-tab-content {
background-color: #f8f8f8;
.form-group {
@ -208,7 +185,7 @@ section.tabs {
}
.col-form-label-sm {
padding-top: 0;
padding-top: 0.2rem;
padding-bottom: 0;
}
@ -261,40 +238,6 @@ section.tabs {
}
}
section.loader {
position: fixed;
z-index: 1;
bottom: 25%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
.spinner {
width: 50px;
height: 50px;
position: relative;
margin: 100px auto;
.double-bounce1,
.double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #999;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;
animation: sk-bounce 2.0s infinite ease-in-out;
}
.double-bounce2 {
animation-delay: -1.0s;
}
}
}
main {
flex: 1 1 auto;
opacity: 0;
@ -317,6 +260,25 @@ main {
.commands {
margin-bottom: 1rem;
line-height: 1.35;
.step {
counter-reset: step-command-counter;
.counter {
display: inline-block;
margin-top: 0.15rem;
&::before {
counter-increment: step-command-counter;
content: counter(step-command-counter);
}
}
& > span:first-of-type .counter {
margin-top: 0;
}
}
}
.file {
@ -344,7 +306,7 @@ main {
code {
display: block;
padding: 0.6rem;
padding: 8px 8px 12px;
-moz-tab-size: 4;
tab-size: 4;
overflow: visible;

View File

@ -5,30 +5,6 @@ aside.sidebar {
margin-top: 0;
}
.github-link {
text-align: center;
font-size: 1rem;
margin-bottom: 0.5rem;
small {
font-size: 90%;
}
img {
height: 1.2rem;
vertical-align: -4px;
}
}
.github-star {
width: 144px;
height: 30px;
display: block;
border: 0;
overflow: hidden;
margin: 0 auto 0.25rem;
}
.buttons {
text-align: center;
margin-top: 1.5rem;

168
resources/scss/_steps.scss Normal file
View File

@ -0,0 +1,168 @@
section.steps {
ol {
display: flex;
flex-direction: row;
list-style: none;
counter-reset: steps-counter;
padding: 60px 0 0;
&[data-active-step="download"],
&[data-active-step="ssl"],
&[data-active-step="certbot"],
&[data-active-step="live"] {
li.download {
div {
background-color: #000;
border-color: #000;
svg {
stroke: #fff;
}
}
}
}
&[data-active-step="ssl"],
&[data-active-step="certbot"],
&[data-active-step="live"] {
li.ssl {
div {
background-color: #000;
border-color: #000;
svg {
stroke: #fff;
}
}
}
}
&[data-active-step="certbot"],
&[data-active-step="live"] {
li.certbot {
div {
background-color: #000;
border-color: #000;
svg {
stroke: #fff;
}
}
}
}
&[data-active-step="live"] {
li.live {
div {
background-color: #000;
border-color: #000;
svg {
stroke: #fff;
}
}
}
}
&[data-active-step="ssl"] {
li.download::after,
li.ssl::before {
background-color: #000;
}
}
&[data-active-step="certbot"] {
li.download::after,
li.ssl::before,
li.ssl::after,
li.certbot::before {
background-color: #000;
}
}
&[data-active-step="live"] {
li.download::after,
li.ssl::before,
li.ssl::after,
li.certbot::before,
li.certbot::after,
li.live::before {
background-color: #000;
}
}
li {
display: block;
position: relative;
flex: 1 1 auto;
counter-increment: steps-counter;
list-style-type: none;
text-align: center;
cursor: pointer;
font-size: 0.9rem;
@include media-breakpoint-up(sm) {
font-size: inherit;
}
&::before,
&::after {
content: '';
display: block;
height: 2px;
width: 50%;
position: absolute;
top: -35px;
background-color: #dee2e6;
z-index: 0;
transition: background-color 0.15s ease-in-out;
}
&::before {
right: 50%;
}
&::after {
left: 50%;
}
&:first-child::before,
&:last-child::after {
display: none;
}
.counter::before {
content: counter(steps-counter) '. ';
font-weight: 700;
}
.circle {
position: absolute;
z-index: 1;
left: 50%;
transform: translateX(-50%);
top: -50px;
width: 40px;
height: 40px;
border: 1px solid #dee2e6;
border-radius: 50%;
background-color: #f8f8f8;
transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
@include media-breakpoint-up(sm) {
top: -60px;
width: 50px;
height: 50px;
}
svg {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60%;
}
}
}
}
}

View File

@ -8,6 +8,7 @@
@import 'body';
@import 'header';
@import 'steps';
@import 'main';
@import 'sidebar';
@import 'footer';