JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://rawgithub.com/Knockout-Contrib/Knockout-Validation/master/Dist/knockout.validation.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
<script type="text/html" id="validationErrorMessage">
    <p data-bind="visible: field.isModified() && !field.isValid(), 
                  text: field.error" class="validationMessage"></p>
</script>

<h1>Abstract Submission Form</h1>
<form id='AbstractForm' data-bind="submit: onSubmit">

    <section>
        <h3>Registration-Id:</h3>
        <!--ko text: id --><!--/ko-->
        <input type="hidden" name="id" data-bind="value: id">
    </section>

    <section>
        <h3>Presentation Title:</h3>
        <textarea data-bind="value: title" name="title"></textarea>
    </section>

    <section>
        <h3>Author List:</h3>
        <table id="authors">
            <tr><th>Presenter</th><th>Initial</th><th>Name</th><th>Affiliations</th></tr>
            <!--ko foreach: authors-->
                <tr data-bind='validationOptions: { insertMessages: false }'>
                    <td> &nbsp; <!--ko text: $index() + 1 + '.' --><!--/ko--> &nbsp; 
                        <input type="radio" name="presenter" data-bind="checked: $parent.presenter, value: $index()"></td>
                    <td><input data-bind="value: initial, attr: {name:'author['+$index()+'].initial', placeholder:  $parent.authorPlaceHolder($index())[0]}"></td>
                    <td><input data-bind="value: name, attr:{name:'author['+$index()+'].name', placeholder:  $parent.authorPlaceHolder($index())[1]}"></td>
                    <td><input data-bind="value: affiliations, attr:{name:'author['+$index()+'].affiliations', placeholder:  $parent.authorPlaceHolder($index())[2]}"></td>
                    <td>
                        <a href="" data-bind="click: $parent.authorRemove,...

CSS

/* リセットしてしまった見出しのサイズ調整 */

h1 {
    font-size: 160%;
}

h2 {
    font-size: 130%;
}

h3 {
    font-size: 100%;
}

/* ここからが AbstractForm 関連 */

form#AbstractForm h3 {
    margin: 20px 0 10px 0;
}

form#AbstractForm textarea[name='title'] {
    width: 450px;
    height: 3em;
}

input.validationError,
select.validationError,
textarea.validationError {
    background-color: #fcc;
}

form#AbstractForm {
    line-height: 140%;
}

form#AbstractForm input,
form#AbstractForm select,
form#AbstractForm textarea {
    font-size: 100%;
}

form#AbstractForm section > :not(h3) {
    margin-left: 20px;
}

form#AbstractForm table#authors tr th,
form#AbstractForm table#authors tr td,
form#AbstractForm table#affiliations tr th,
form#AbstractForm table#affiliations tr td {
    padding: 0 5px 0 5px;
    font-weight: normal;
}

form#AbstractForm table#authors tr th,
form#AbstractForm table#affiliations tr th {
    vertical-align: top;
}

form#AbstractForm table#authors tr td:nth-child(2) input {
    width: 60px;
}

form#AbstractForm table#authors tr td:nth-child(3) input {
    width: 150px;
}

form#AbstractForm table#authors tr td:nth-child(4) input {
    width: 80px;
}

form#AbstractForm table#affiliations tr td:nth-child(2) input {
    width: 300px;
}

form#AbstractForm table#affiliations tr td:nth-child(3) input {
    width: 100px;
}

form#AbstractForm section#preview div.title {
    font-weight: bold;
}

form#AbstractForm section#preview div.authors,
form#AbstractForm section#preview div.affiliations {
    font-style: italic;
}

form#AbstractForm section#preview > div.authors > span.presenter {
    text-decoration: underline;
}

form#AbstractForm section#preview > div.authors > span.author > span.affiliations {
    vertical-align: super;
    font-size: small;
}

form#AbstractForm section#preview > div.authors > span.presenter > span.affiliations {
    text-decoration: none !important;
}

form#AbstractForm section#preview div.affiliations span.affiliation span.number {
   ...

JavaScript

var Author = function(form, initial, name, affiliations){
    var self = this;
//    self.form = form;                         // これをすると依存関係の検出時に無限ループに落ち込む
//    self.form = function(){ return form; }    // これならOK
    self.initial = ko.observable(initial).extend({ 
                    required: true,
                    pattern: {
                        message: 'First and middle names should be typed as initial(s) '+
                                 'that is expected to have a period character "." at the end.',
                        params: '\\.$'
                    }
                });
    self.name = ko.observable(name).extend({ required: true });
    self.affiliations = ko.observable(affiliations).extend({ 
                    required: true,
                    pattern: {
                        message: 'Affiliation must be specified by comma separated numbers.',
                        params: '^ *[1-9][0-9]*( *, *[1-9][0-9]*)* *$'
                    },
                    validation: [ {
                        validator: function(val) {
                            var ok = true;
                            _.each(val.split(/ *, */), function(a) {
                                if(Number(a) > form.affiliations().length ||
                                    (Number(a) == 0) )
                                    ok = false;
                            });
                            return ok;
                        },
                        message: 'Specified affiliation number does not exist.'
                    }, {
                        validator: function(val) {
                            var as = _.sortBy(
                                        _.map( val.split(/ *, */), 
                                               function(a) { return Number(a); }), 
                                        function(a) {return a});
                            for(var i=0; i<as.length-1; i++)
                                if(as[i]==as[i+1])
       ...