Blog-Archiv

Freitag, 25. Januar 2019

Git Commands and Flow

I used SCCS, RCS, CVS, SVN, and all these version control systems (VCS) were getting better and better over the years, especially SVN was nearly perfect in the end.

Now a tool with the beautiful name Git has come into vogue, created in 2005 by Linus Torvalds. End of 2018 it made up 61% of all VCS installations. Some say this is a big step forward, some say it is two steps back.

Using Git definitely requires more time than the old VCS, because it is much more complex. It was made to provide access to the LINUX source code for thousands of developers, not for small companies that have ten developers and never would open their source for public work. Long-termed collaboration of many people also requires a strategy, something like Vincent Driessen's Git-Flow, because it is based on branches.

In this Blog I will list some basic command sequences that may be useful for all who need to use git. For simulation of multiple user collaboration I could not find a compound try-out site on the Internet, so you would have to (patiently) install a git server on your machine when you'd like to test this.


Basics

For all commands shown here (except clone) you need to stand in the root directory of your local git-project.

Participate in a Project

git clone projectUrl

Somewhere on the web-page of the git project of your choice you will find the projectUrl you need to check-out (sorry, clone).

Commit to Remote

After cloning, your current branch is the master branch. This is something like a holy grail, and most likely you won't be able to commit to it. But assumed you can, do the following to save your changes to the project:

git commit -am "Write some check-in comment here."
git push origin master

The commit command just saves into you local git store. The -am option is an abbreviation for -a -m, whereby the -a ("add") will cause all newly created files to be added (staged), and the -m ("message") is the opener for a comment text. If you dropped the -a, you'd have to launch a "git add ." before the commit, standing in the project's root directory.

What was commit in old VCS is now push. The parameter origin is a git-convention designating the remote repository (URL) where the project came from. The name of the branch that will be updated is master.

After this, your colleagues will be able to see your changes as soon as they update their project-clone (that is also on branch master).

Update from Remote

git fetch

This updates your local git (not your source code!) with knowledge about e.g. new branches on the repository. This is not always needed, but sometimes you should do it.

git pull origin master

You "pull" changes from the remote project's master branch and merge them into your (possibly modified) sources. Conflicts can occur.

→ Mind that it is very recommendable to update (sorry, pull) your sources before you commit (sorry, push) your changes!

What is a Pull Request?

As you mostly can not push to master branch, you ask someone having permission to do it.
Launch a browser and go to the web-page of the remote repository. Choose your branch. Find the button to open a pull request. Fill out the form and submit.


Frequent Commands

git status   # displays local changes

git branch   # lists all branches known locally

git checkout branch-name   # changes from current to named branch 

git checkout -b new-branch-name   # creates a new branch and makes it the current one

git merge branch-to-merge   # merge another branch into current branch 

# delete a branch from local and remote
git branch -d branch-name
git push origin --delete branch-name

git reset --hard HEAD   # dismiss all local changes and commits and replace everything with remote sources, use any hash-id from 'git log' instead of HEAD


Collaborative Work

Following commands resemble Vincent Driessen's Git-Flow.

  1. Two immortal branches exist: master and develop, from which
    master is the best, you can always branch and release from master, and
    develop is more advanced, but may be unstable, anyway it is the release-candidate

  2. Two kinds of temporary mortal branches exist: feature-xxx, hotfix-xxx,
    the first for long-lasting work, the second for small corrections

  3. More and more from time to time created immortal branches will exist: release-X.X.X.
    During release, bugfixes are done directly on the release-X.X.X branch, without further branching. Finally it is merged into master and develop. Release-branches are immortal because they could be deployed at customers that don't want to upgrade, but are reporting bugs.

This is the flow for hotfixes:

  • branched from master
  • get merged into master via pull request (to enable quality assurance)
    • every fix increases the 3rd version number (semantic versioning)
  • must be merged into develop when pull request was accepted
  • in case a release is running, fixes also must be merged into the release branch
  • fix branches get deleted after all

And this is the flow for features and releases:

  • features are branched from develop
  • get merged back into develop
  • release is branched from develop when features need to be deployed
    • features not being in develop at that time won't be released
  • release gets fixed, without branching, until quality assurance agrees
  • fully tested release branch gets merged into master and develop
    • every feature release increases the 2nd version number (semantic versioning)
  • all released feature branches and their release branch get deleted after merge

In following, please replace branch-name with a human-readable identifier that designates the issue you are solving with this branch.

Start Feature

git checkout develop
git pull origin develop
git checkout -b branch-name

Finish Feature

git commit -am "Write a comment here"
git checkout develop
git pull origin develop
git merge --no-ff branch-name
git push origin develop


Start Hotfix

git checkout master
git pull origin master
git checkout -b branch-name

Finish Hotfix of Version 1.0.0

git commit -am "Write a comment here"
git checkout master
git pull origin master
git merge --no-ff branch-name
git tag -am "Write a comment here" 1.0.1
git push origin develop
git push --tags origin develop
git checkout develop
git pull origin develop
git merge --no-ff branch-name
git push origin develop


Start Release Branch off 1.0.23

git checkout develop
git pull origin develop
git checkout -b release-1.1.0
git push origin release-1.1.0

Bugfix Release Branch

git checkout release-1.1.0

Change sources, then do:

git commit -am "Explanation of bugfix"
git push origin release-1.1.0
git checkout develop
git pull origin develop
git merge --no-ff release-1.1.0
git push origin develop

Finish Release Branch to 1.1.0

git checkout master
git pull origin master
git merge --no-ff release-1.1.0
git tag -am "Explanation of release" 1.1.0
git push origin master
git push --tags origin master
git checkout develop
git pull origin develop
git merge --no-ff release-1.1.0
git push origin develop 



Resume

For collaborative work inside a protected domain, where everybody frequently updates and commits sources, SVN would be the better system, because it is much simpler than git.
For development that is done across the world in long-lasting tasks, git would prevail.
So make you choice. There won't come anything any more after git.




Mittwoch, 9. Januar 2019

Canon Scanner on LINUX Shows Vertical Bar

I spent an afternoon staring at LINUX forums to get my scanner working. There was a bug introduced in Ubuntu 18.04 that hit Canon scanners, mine is a CanoScan LIDE 200. Symptom was that there was a thick vertical bar in the middle of the image, being black when scanning grey, else being randomly coloured. It is a known bug, nevertheless a system-update did not fix it, because the fix might open a bug for another type of scanner (at least this is what I understood from the forums).

I use the graphical xsane scanner tool, which is a frontend for the sane-genesys backend drivers. The bug was reported to be in the backend, which is called "libsane1". To fix it I followed the advices given on a launchpad web page, it was the entry of date 2018-12-01, written by gary17, that helped. But of course not without problems.

Commands to fix libsane1 on Ubuntu 18.04

Following were the commands I launched, in a terminal window, to get my scanner working.

sudo apt-get install dpkg-dev fakeroot

This installs two needed libraries, unless they are already installed. They serve for compiling and building Debian packages (Ubuntu is a Debian derivate).

cd $HOME/programs/
mkdir sanebackend-1.0.27-fix
cd sanebackend-1.0.27-fix
apt-get source libsane1

We need to get the current source code of the genesys drivers, here I create a directory where to put these sources, and change into it. Then I download the sources by apt-get, not as super-user because this is not needed to compile them, and I don't want files in my user-directory that belong to root.

A decent developer already found the problem and published the solution. So we just need to patch the source, here is the diff-text:

2075c2075,2079
<   if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK && dev->model->cmd_set->rewind)
---
>   if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK && dev->model->cmd_set->slow_back_home)
>     {
>       status = dev->model->cmd_set->slow_back_home (dev, dev->model->flags);
>     }
>   else if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK && dev->model->cmd_set->rewind)

Save this into file genesys.c.diff, or store the contents of this link into it.

patch sane-backends-1.0.27/backend/genesys.c < genesys.c.diff

This command fixes the source code using the diff-file. Now we need to build and install.

sudo apt-get build-dep libsane1

This prepares all system-dependencies for package libsane1 so that we can build it. build-dep is an apt-get command just like install and remove.

cd sane-backends-1.0.27
./configure
dpkg-buildpackage -rfakeroot -uc -b

Here I change into the source-directory of libsane1 and "configure" it, which is a platfom-preparation of the "build". Makefiles get generated. This takes a while.

The final dpkg-buildpackage command starts the compilation. This again will take some time, and output a lot of compiler messages.

echo $?

This command outputs the exit-code of the latest command, in this case the compilation. If it is NOT zero (0 is positive!), something went wrong. I got 2 as exit code, and saw following error message:

....
kpathsea: Running mktexmf ptmr7t
! I can't find file `ptmr7t'.
....

This happened on generating "manpages", i.e. the UNIX documentation for the library. As I did not know how I could bypass this, and the new driver was not built due to this failing sub-build, I tried to find hints about this error message. I found out that it originates from TeX statements like the following:

\usepackage{times,mathptmx,courier}

And I found out that the times font is obsolete. So a missing font for the documentation made the build of a scanner driver fail! Was it called dependency-hell :-?

I searched where the font was used, and found it just once in file doc/sane.tex in line 3:

\documentclass[11pt]{report}

\usepackage{times,graphicx,url}
% ....

So I searched for another font that I could apply there, and I found it on a TeX forum. I edited the doc/sane.tex file and replaced "times" by "helvet". Then I ran again the compilation:

dpkg-buildpackage -rfakeroot -uc -b

Yes, it failed again. This time because of a missing m4/libtool.m4 file. I tried to run ./configure again, but there was no such script any more. Instead of starting all over I found a workaround by copying an existing libtool.m4:

cp /usr/share/aclocal/libtool.m4 m4/

Another compilation now finished successfully:

dpkg-buildpackage -rfakeroot -uc -b
.....
echo $?
0

The exit code 0 reports a successful build. The resulting Debian-package was in ../libsane-dev_1.0.27-1~experimental3ubuntu2_amd64.deb. I installed this package into the operating-system by following command:

cd ..
sudo dpkg -i libsane1_1.0.27-1~experimental3ubuntu2_amd64.deb

It finished successfully. Now I started xsane and tried to scan a page, without rebooting the system:

No vertical bar showed any more!

To protect my fix against system-updates I launched this command:

sudo apt-mark hold libsane1

Response was: libsane1 set on hold. Hope this will be respected by the Ubuntu updater! Else I will have to come back here and do the same again, until they find out how to fix this bug for all types of scanners.

Resume

Using an open-source operating system may cause lost afternoons :-)

But why did I "stare" at the LINUX forums?
Because these entries are missing quality, they ...

  • talk too much jargon, slang, lingo, gobbledygook
  • don't mention the risks, e.g. when downgrading a library
  • don't explain the command lines, and what they are for
  • don't tell the expected outcome, i.e. how to check whether a command worked or not
  • are incomplete sometimes

I spent 90% of that afternoon trying to understand instructions and considering risks, only 10% with real editing work. So, does quality of communication count?




Dienstag, 1. Januar 2019

HTML Form Layout CSS

The HTML form element does nothing to arrange its subordinates in an appropriate layout. We must do it ourselves.
Such layout expectations could be specified as:

  • Fields and Sets:

    • A form can contain a mix of fields and field-sets, where a field-set is a titled vertically arranged group of fields.

  • Horizontal Layout:

    • Portrait-format (mobile): one-column table, label on top, input field below, both in same table-cell.
      • Exception checkbox: label right, box left.

    • Landscape-format (desktop): two columns, label in left column, input field in right.
      • Label is right aligned, so that it is near to the field it describes.
      • Exception checkbox: label right, box left.
      • Newspaper layout: to exploit width, distribute fields across several horizontally ordered boxes.

  • Vertical Layout:

    • A left-side label should be on same height as its right-side input field, vertically aligned "middle".
    • In case the field or field-set is much higher than its left-side label, the label should be aligned to the upper edge of the field or field-set.

  • Nested Layouts:

    • A field can be organized as several fields with its own layout, examples are "Date of Birth" with "Age" output, or "Year", "Month" and "Day" input fields in a row.

In this article I will introduce CSS implementing this. I won't yet cover the checkbox case, and also I won't think about "newspaper layout" (distributing label/field pairs across several columns when having a restricted height). You can visit the current state of this project on my homepage.

Form Sample

Here is a HTML form example that contains all fields available from HTML-5 browsers, so that we can examine whether the layout works for all types of fields. This is also to try out the field-features your browser provides.

E.g. Firefox doesn't support datetime-local, week, month fields by now.
Chrome supports all, but paints box-shadow for radio-buttons badly, and the default shape of fields is ugly.

Use the button to toggle the layout from desktop ("left-label") to mobile ("top-label").
Use the button to stretch the form to full width.

HTML Form Layout Example
 

Notice that required field labels are also bold for field-sets, in case the set contains at least one required field. Example is the "More Text" group. On the other hand there is no required field in the "Date / Time" group, thus its label is not bold.

Core Form Layout CSS

I will present this in small doses. You can find the entire source code of the example on bottom of the article.

Most important to know is that I used line-height to get rid of vertical alignment problems (still open problems!), and box-sizing: border-box; to stop fields hanging out at right side.

About CSS coding:
I recommend to write comments on any CSS line, at least one per rule-set. Only this keeps CSS code maintainable! Remember that a rule like position: relative; can serve several purposes, so we need to make explicit why we apply it.

Basic Label Styling

      /* Smaller sans-serif font. */
      form label
      {
        font-size: 76%;
        font-family: sans-serif;
      }

      /* Add colon after any label. */
      form label::after
      {
        content: ":";
      }
      /* But not on label after radio. */
      form input[type = 'radio'] + label::after
      {
        content: "";
      }

The label should have a smaller font than the field. This also will uncover vertical alignment problems in the layout.
Further this CSS sets a colon after the label, and avoids it for radio buttons where the label is right of the button.

Labels of Required Fields

The JavaScript I introduced in my recent Blog copies the attribute required from field to label, the CSS below relies on that.

      /* Set asterisk before required fields. */
      form label[required]::before
      {
        content: "* ";
      }
      
      /* Make labels for required fields better visible. */
      form label[required]
      {
        font-weight: bold;
      }

This is for setting an asterisk (*) before any label of a required field, and displaying it in a bold font.

DIV Table Layout

      form .table
      {
        display: table;
        width: 100%;  /* if parent is 100%, go with it */
      }
      
      form .table > div
      {
        display: table-row;
        line-height: 2em;  /* needed for vertical line-alignment */
      }

This defines the table-layout inside the form, not including the "Submit" and "Reset" buttons. It will always fill its parent's width fully, so you should wrap it into either a display: inline-block; (natural width) or a display: block; (full width) DIV container.

The table-row receives its line-height here, important to avoid vertical alignment problems. The table-cell is not yet defined, it will be set specifically later for either mobile or desktop.

Layout of Fields Inside Tables

      /* Stretch fields to full cell width, except checkboxes and radios. */
      form .table > div > input:not([type = 'radio']):not([type = 'checkbox']),
      form .table > div > textarea,
      form .table > div > select
      {
        width: 100%;
      }

      /* Avoid text fields hanging out at right due to content-box sizing. */
      form .table input,
      form .table textarea,
      form .table select
      {
        box-sizing: border-box;
      }
      
      /* Let the user resize big fields vertically. */
      form .table select[multiple],
      form .table textarea
      {
        resize: vertical;
      }

Fields should fill their cell's full width, not keep their "natural" width which is browser-specific.
Force the browser to calculate their border-box, not content-box, else they will hang out at right side.
Optionally we should allow the user to resize choosers that may contain lots of rows, like <textarea> and <select multiple>.

Left Label for Desktop

      form .left-label > div > label,
      form .left-label > div > input,
      form .left-label > div > select,
      form .left-label > div > textarea,
      form .left-label > div > div,
      form .left-label > div > fieldset
      {
        display: table-cell;  /* display elements horizontally */
        vertical-align: middle;  /* avoid vertical alignment problems */
        
        margin-top: 0.2em;  /* vertical line distance */
        margin-bottom: 0.2em;
      }
    
      form .table.left-label > div label
      {
        text-align: right;
        padding-right: 0.2em;  /* distance to right field */
        
        vertical-align: top;   /* necessary for labels of nested fieldsets */
      }

So here is a list of element names that can be inside a table-cell. For a desktop-suited table with two columns, all these elements should behave as table-cell. We allow beside input, select and textarea also fieldset and div elements.
Their vertical alignment should be "middle" by default.
To create a little space between layout rows there is margin at bottom.

Labels should be aligned right in left-label variant, to be as near as possible to the field, but nevertheless keep a little padding-distance.
For labels we overwrite the default vertical alignment with "top" here, because in case a whole fieldset (high) is on right side, we want the label to stay in a visible range.


Mind that there are no further rules for the top-label layout variant. As any input field has been stretched to 100%, its preceding label will automatically go to top. As a side-effect top-label will be the default, unless you put a CSS class="left-label" onto the DIV table root-element.


Optional Additions

Following introduces useful code to style initial validation, button bars and nested right-side containers (horizontal bars).

Visible Initial Validation

      form :not(output):not(fieldset):invalid /* fieldset would also be red */
      {
        box-shadow: 1px 1px 1px red;
      }

If you want to make the validation markers already visible on document-load, add this rule-set. By default HTML forms would be validated by the browser only on a submit-event, and only then you would see the browser-made validation markers and tips.

Buttons and Toolbar

      form input[type = 'submit'],
      form input[type = 'reset']
      {
        margin-top: 0.4em;  /* space to form above */
        font-size: 140%;  /* using Unicode character as icon */
        width: 33%;  /* take a third of available space */
      }
      
      form input[type = 'submit']
      {
        color: green;
      }
      form input[type = 'reset']
      {
        color: red;
      }
      
      /* Variant label-top: put "Ok" to very right, "Cancel" will be at very left. */
      form :not(.left-label) + .button-container input[type = 'submit']
      {
        float: right;
      }
      
      /* Variant label-left: align "Cancel" and "Ok" to middle. */
      form .left-label + .button-container
      {
        text-align: center;
      }

Try out of you like this appearance of buttons and their bar. It is different for top-label and left-label variants. Such things depend on ergonomics, current fashion, company-rules, and last not least customer wishes. The "Submit" button right now establishes slowly, but still buttons top or bottom is undecided.

Horizontal Layout

      .horizontal-bar  /* flex-direction row */
      {
        display: flex !important;  /* else overwritten by table-cell! */
        align-items: center;  /* do not stretch vertically */
      }
    
      .horizontal-bar-natural  /* natural size */
      {
        flex: initial;
      }
      .horizontal-bar-stretch  /* takes all available space */
      {
        flex: auto;
      }

This is a general purpose layout done through flexbox. Look at the "Date" - "Years Since" fields above for an example. It is a frequent case that you have several fields in a line, and one of them should take the most available space. Put a CSS class="horizontal-bar" onto the container DIV, and one of class="horizontal-bar-natural" (just once!) or class="horizontal-bar-stretch" onto the elements in row.

General Purpose CSS

      /* Enable a fluid responsive display of forms and fieldsets. */
      div.responsive
      {
        display: inline-block; /* take just the space needed */
        vertical-align: top; /* align to top if any peer DIV is present */
      }
      
      /* Keep label and radio-button always on same layout row. */
      .sameline
      {
        white-space: nowrap;
      }

      /** Round border for fieldset. */
      fieldset
      {
        border-radius: 0.5em;
      }

If you want DIV containers to arrange themselves responsively side-by-side, you can set a CSS class="responsive" into it.

The CSS class="sameline" is necessary to tie radio buttons to their label in a way that never the label is in a different layout row than the button. This could be quite misleading! Look at this example:

The same when <span class="sameline"> was wrapped around label and input:


Entire Source Code

Press the arrow-button to see HTML and CSS source code of this example.

Click to see example source.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <title>HTML Form Test</title>
    
    <!-- Core form layout -->
      
    <style> /** Style labels of fields. */
    
      /* Smaller sans-serif font. */
      form label
      {
        font-size: 76%;
        font-family: sans-serif;
      }
      
      /* Add colon after any label. */
      form label::after
      {
        content: ":";
      }
      /* But not on label after radio. */
      form input[type = 'radio'] + label::after
      {
        content: "";
      }
    </style>
    
    <style> /** Style labels of required fields. */
    
      /* Set asterisk before required fields. */
      form label[required]::before
      {
        content: "* ";
      }
      
      /* Make labels for required fields better visible. */
      form label[required]
      {
        font-weight: bold;
      }
    </style>
    
    <style> /** DIV table layout. */
    
      form .table
      {
        display: table;
        width: 100%;  /* if parent is 100%, go with it */
      }
      
      form .table > div
      {
        display: table-row;
        line-height: 2em;  /* needed for vertical line-alignment */
      }
    </style>
    
    <style> /** Layout fields inside tables. */
    
      /* Stretch fields to full cell width, except checkboxes and radios. */
      form .table > div > input:not([type = 'radio']):not([type = 'checkbox']),
      form .table > div > textarea,
      form .table > div > select
      {
        width: 100%;
      }

      /* Avoid text fields hanging out at right due to content-box sizing. */
      form .table input,
      form .table textarea,
      form .table select
      {
        box-sizing: border-box;
      }
      
      /* Let the user resize big fields vertically. */
      form .table select[multiple],
      form .table textarea
      {
        resize: vertical;
      }
    </style>
    
    <style> /** Layout for left-label variant */
      
      form .left-label > div > label,
      form .left-label > div > input,
      form .left-label > div > select,
      form .left-label > div > textarea,
      form .left-label > div > div,
      form .left-label > div > fieldset
      {
        display: table-cell;  /* display elements horizontally */
        vertical-align: middle;  /* avoid vertical alignment problems */
        
        margin-top: 0.2em; margin-bottom: 0.2em; /* vertical line distance */
      }
    
      form .table.left-label > div label
      {
        text-align: right;
        padding-right: 0.2em;  /* distance to right field */
        
        vertical-align: top;   /* necessary for labels of nested fieldsets */
      }
    </style>

    <style> /* Layout for label-above variant. */
      /*
       * Because any input field has been stretched to 100%, the
       * preceding label will automatically go to top. Nothing to do!
       */
    </style>
    
    
    <!-- Optional additions -->

    <style> /** Make validation visible initially. */
    
      form :not(output):not(fieldset):invalid /* fieldset would also be red */
      {
        box-shadow: 1px 1px 1px red;
      }
    </style>
    
    <style> /* Button and toolbar styles. */
    
      form input[type = 'submit'],
      form input[type = 'reset']
      {
        margin-top: 0.4em;  /* space to form above */
        font-size: 140%;  /* using Unicode character as icon */
        width: 33%;  /* take a third of available space */
      }
      
      form input[type = 'submit']
      {
        color: green;
      }
      form input[type = 'reset']
      {
        color: red;
      }
      
      /* Variant label-top: put "Ok" to very right, "Cancel" will be at very left. */
      form :not(.left-label) + .button-container input[type = 'submit']
      {
        float: right;
      }
      
      /* Variant label-left: align "Cancel" and "Ok" to middle. */
      form .left-label + .button-container
      {
        text-align: center;
      }
    </style>
    
    <style> /** General purpose horizontal bar. */
    
      .horizontal-bar  /* flex-direction row */
      {
        display: flex !important;  /* else overwritten by table-cell! */
        align-items: center;  /* do not stretch vertically */
      }
    
      .horizontal-bar-natural  /* natural size */
      {
        flex: initial;
      }
      .horizontal-bar-stretch  /* takes all available space */
      {
        flex: auto;
      }
    </style>
    
    <style> /** General purpose layout styles. */
    
      /* Enable a fluid responsive display of forms and fieldsets. */
      div.responsive
      {
        display: inline-block; /* take just the space needed */
        vertical-align: top; /* align to top if any peer DIV is present */
      }
      
      /* Keep label and radio-button always on same layout row. */
      .sameline
      {
        white-space: nowrap;
      }
      
      /** Round border for fieldset. */
      fieldset
      {
        border-radius: 0.5em;
      }
    </style>
    

    <!-- Script to copy required and ids -->
    
    <script>
      /**
       * Avoid duplication of "required" and "id" attributes in HTML forms.
       * Any LABEL before an INPUT must get the INPUT's "id" value into its "for" attribute. 
       * Any LABEL before an INPUT must copy the INPUT's "required" if it exists. 
       * Sometimes the "required" attribute is not in the next element but in its sub-DOM.
       */
      function manageIdAndRequired()
      {
        var count = 1;  /* sequence number for unique ids */
        
        /** Searches for "required" in given element and all sub-elements. */
        function containsRequired(element) {
          if (element.getAttribute("required") !== null)
            return true; 
          
          var children = element.children;
          for (var i = 0; i < children.length; i++)
            if (containsRequired(children[i]))
              return true;
          
          return false;
        }
        /* end containsRequired() */
        
        /** Copies the optional "required" attribute to LABEL, chains it with INPUT's id. */
        function chainLabelToIdAndCopyRequired(label) {
          var nextSibling = label.nextElementSibling;
          var nextTagName = nextSibling ? nextSibling.tagName : undefined;
          
          /* previous is just for radio-buttons */
          var previousSibling = label.previousElementSibling;
          var previousTagName = previousSibling ? previousSibling.tagName : undefined;
          var isRadioLabel = (previousTagName === "INPUT");
          
          if (isRadioLabel ||
                nextTagName === "INPUT" ||
                nextTagName === "SELECT" ||
                nextTagName === "TEXTAREA" ||
                nextTagName === "DIV" ||
                nextTagName === "FIELDSET")
          {
            if ( ! isRadioLabel && containsRequired(nextSibling))
              label.setAttribute("required", "");  /* copy attribute to non-radios */
            
            if (label.getAttribute("for") === null) {  /* chain id */
              var sibling = isRadioLabel ? previousSibling : nextSibling;
              var id = sibling.getAttribute("id");
              
              if ( ! id && sibling.getAttribute("name")) {  /* generate id from name */
                var identity = "_"+count;
                count++;
                id = sibling.getAttribute("name")+identity;
                sibling.setAttribute("id", id);
              }
              
              if (id)
                label.setAttribute("for", id);
            }
          }
        }
        /* end chainLabelToIdAndCopyRequired() */
        
        /* Iterate all forms on page */
        var forms = document.getElementsByTagName("form");
        for (var f = 0; f < forms.length; f++) {
          var elementsInForm = forms[f].getElementsByTagName("*");
          for (var i = 0; i < elementsInForm.length; i++) {
            var element = elementsInForm[i];
            if (element.tagName === "LABEL")
              chainLabelToIdAndCopyRequired(element);
          }
        }
      }
      
      /* Execute function on page-load */
      window.addEventListener("load", manageIdAndRequired);
      
    </script>
    
  </head>
  
  
  <body>
  
    <form>
      <div class="responsive">
        <fieldset>
          <legend>HTML Form Layout Example</legend>
    
          <div class="table">
          <!-- <div class="table left-label"> -->
          
            <div>
              <label>Single Line</label>
              <input name="single-line-text" type="text" required value="Default value in text-field">
            </div>
          
            <div>
              <label>Password</label>
              <input name="hidden-password" type="password" required>
            </div>
          
            <div>
              <label>Number</label>
              <input name="numeric" type="number" required>
            </div>
          
            <div>
              <label>More Text</label>
              
              <fieldset>
                <div class="table left-label">
              
                 <div>
                    <label>E-Mail</label>
                    <input name="e-mail" type="email" required value="a@b.c">
                  </div>
                
                  <div>
                    <label>Telephone</label>
                    <input name="phone-number" type="tel" />
                  </div>
                
                  <div>
                    <label>URL</label>
                    <input name="url-address" type="url" value="protocol://host">
                  </div>
                
                  <div>
                    <label>Auto-Suggest</label>
                    <input list="browsers" name="auto-suggest"/>
                    <datalist id="browsers">
                      <option value="Firefox">
                      <option value="Chrome">
                      <option value="Opera">
                      <option value="Safari">
                      <option value="Edge">
                    </datalist>
                  </div>
                  
                  <div>
                    <label>Search</label>
                    <input name="search-pattern" type="search">
                  </div>
                
                  <div>
                    <label>Multiple Lines</label>
                    <textarea name="multiline-text" required>Default
value
in
text
area</textarea>
                  </div>
            
                </div> <!-- end table -->
              </fieldset> <!-- end cell -->
            </div> <!-- end row -->
          
            <div>
              <label>Date</label>
              <div class="horizontal-bar">
                <script>
                  function calculateYearsSince(date) {
                    var millis = Date.now() - new Date(date).getTime();
                    var years = millis / 1000 / 60 / 60 / 24 / 365;
                    var age = (years > 0) ? Math.floor(years) : Math.ceil(years);
                    document.getElementById("yearsSince").value = age;
                  }
                </script>
                <input class="horizontal-bar-stretch" name="day-only" type="date" required oninput="calculateYearsSince(this.value);">
                &nbsp;
                <label class="horizontal-bar-natural" for="yearsSince">Years Since</label>
                <output name="yearsSince" id="yearsSince"></output>
              </div>
            </div>
          
            <div>
              <label>Date / Time</label>
              
              <fieldset>
                <div class="table left-label">
                
                  <div>
                      <label>Time</label>
                      <input name="time-only" type="time">
                  </div>
                  
                  <div>
                      <label>Date / Time Local</label>
                      <input name="date-and-time-local" type="datetime-local">
                  </div>
                  
                  <div>
                      <label>Month</label>
                      <input name="month-only" type="month">
                  </div>
                  
                  <div>
                      <label>Week</label>
                      <input name="week-only" type="week">
                  </div>
                  
                </div> <!-- end table -->
              </fieldset> <!-- end cell -->
            </div> <!-- end row -->
          
            <div>
              <label>Other Inputs</label>
              
              <fieldset>
                <div class="table left-label">
                
                  <div>
                      <label>Radiobuttons</label>
                      <div> 
                        <!-- span.sameline: don't let the label and the button separate to different layout rows -->
                        <span class="sameline"><input name="gender" type="radio" value="male"   required><label>Male</label></span>
                        <span class="sameline"><input name="gender" type="radio" value="female" required><label>Female</label></span>
                        <span class="sameline"><input name="gender" type="radio" value="other"  required><label>Other</label></span>
                      </div>
                  </div>
                  
                  <div>
                      <label>More Radiobuttons</label>
                      <div>
                        <!-- div: display each radio-button on a new layout row -->
                        <div><input name="RGB" type="radio" value="red"  ><label>Red</label></div>
                        <div><input name="RGB" type="radio" value="green"><label>Green</label></div>
                        <div><input name="RGB" type="radio" value="blue" ><label>Blue</label></div>
                      </div>
                  </div>
      
                  <div>
                      <label>Single Select</label>
                      <select name="single-select" required>
                        <optgroup label="Compiled">
                          <option value="java">Java</option>
                          <option value="c++">C++</option>
                        </optgroup>
                        <optgroup label="Interpreted">
                          <option value="es6">EcmaScript</option>
                          <option value="python">Python</option>
                        </optgroup>
                      </select>
                  </div>
                  
                  <div>
                      <label>Multiple Select</label>
                      <select name="multi-select" multiple required>
                        <option value="one">Use</option>
                        <option value="two">Shift</option>
                        <option value="three">and</option>
                        <option value="four">Control</option>
                        <option value="five">Keys</option>
                        <option value="six">with</option>
                        <option value="seven">Mouse</option>
                      </select>
                  </div>
                  
                  <div>
                      <label>Range</label>
                      <input name="slider" type="range">
                  </div>
                  
                  <div>
                      <label>Color</label>
                      <input name="colour" type="color">
                  </div>
                  
                  <div>
                      <label>File</label>
                      <input name="file-upload" type="file">
                  </div>
                  
                </div> <!-- end table -->
              </fieldset> <!-- end cell -->
            </div> <!-- end row -->
          
            <div>
              <label>License Read</label>
              <input name="boolean" type="checkbox" required>
            </div>
          
          </div> <!-- end table -->
          
          
          <div class="button-container">
            <input title="Reset" type="reset" value="&#x2718;">
            <input title="Submit" type="submit" value="&#x2714;">
          </div>
        
        
        </fieldset> <!-- end top fieldset -->
      </div> <!-- end div.responsive -->
          
    </form>
    
  </body>
</html>