Search code examples
htmlcssinputlabelcenter

Ignore label elements when center-aligning form


I am creating a form for my site that is center-aligned on my page. However, I want to ignore the labels when center aligning, such that the input boxes are center-aligned, with the labels right aligned up-against the inputs.

Here is a simplified snippet from my code, just to display the issue.

body {
text-align: center;
}
<!DOCTYPE html>
<html>
<body>

<h1>Login</h1>

<form>
    <label for="email">Email:</label>
    <input type="email" id="email" name="email" required>

    <br><br>

    <label for="password">Password:</label>
    <input type="password" id="password" name="password" required>

    <br><br>

    <button class = 'button' id ='loginbutton' type="submit">Login</button>
</form>

</body>
</html>

By reading other questions, I saw one recommendation of setting the display to inline block and the width to 0. That properly aligned my input boxes, but my labels are now behind the text.

body {
text-align: center;
}

label {
display: inline-block;
width: 0;
}
<!DOCTYPE html>
<html>
<body>

<h1>Login</h1>

<form>
    <label for="email">Email:</label>
    <input type="email" id="email" name="email" required>

    <br><br>

    <label for="password">Password:</label>
    <input type="password" id="password" name="password" required>

    <br><br>

    <button class = 'button' id ='loginbutton' type="submit">Login</button>
</form>

</body>
</html>

I know I could achieve this through a table, but I was wondering if there was any simpler way? Here is the code showing my desired look.

body {
    text-align: center;
 }

#table-form {
    display: flex;
    justify-content: center;
    text-align: right;
 }

 td {
    width: 10rem
 }
<!DOCTYPE html>
<html>
<body>

<h1>Login</h1>

<form>
  <div id = 'table-form'>
    <table>
       <tr>
          <td> <label for="email">Email:</label> </td>
          <td> <input type="email" id="email" name="email" required> </td>
          <td> </td>
        </tr>
        
        <tr>
          <td> <label for="password">Password:</label> </td>
          <td> <input type="password" id="password" name="password" required> </td>
          <td> </td>
        </tr>
     </table>
   </div>
   
   <br>
   
   <button class = 'button' type="submit">Login</button>

</form>

</body>
</html>

If the table is the best way to address this, just let me know :) Thanks for the help!


Solution

  • One approach, with explanatory comments in the code, is below:

    /* we use this width a couple of times, so we cache it as
       a CSS custom property (to allow for easier updating in
       the future): */
    :root {
      --inputWidth: 15em;
    }
    
    
    /* hugely simple/naive reset, to have all elements' dimensions
       calculated as including the width of borders and padding, and
       removing default margin and padding: */
    *,
     ::before,
     ::after {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    
    
    /* to center align: */
    h1 {
      text-align: center;
    }
    
    
    /* using flex-layout: */
    form {
      display: flex;
      /* shorthand for:
          flex-direction: column;
          flex-wrap: nowrap;
      */
      flex-flow: column nowrap;
      /* shorthand for:
          justify-content: center;
          align-content: center;
      */
      place-items: center;
      /* to place a 2em gap between adjacent elements,
         whether horizontally or vertically; shorthand for:
          gap: 2em 2em;
         which is shorthand for:
          column-gap: 2em;
          row-gap: 2em;
      */
      gap: 2em;
      /* applies a margin along the block axis (in a
         language such as English that's top and bottom): */
      margin-block: 1em;
      /* applies an auto margin along the inline axis (in a
         language such as English that's left and right), this
         causes the <form> element to be centered on the inline
         axis: */
      margin-inline: auto;
      /* using the clamp() function to set a preferred width
         of 60vw, with a minimum permitted width of 20em and
         a maximum permitted width of 1000px: */
      width: clamp(20em, 60vw, 1000px);
    }
    
    label {
      /* centering the content of the <label> element: */
      justify-content: center;
      /* using flex layout: */
      display: flex;
      /* and assigning a position of relative, in order
         to absolutely position a descendant: */
      position: relative;
    }
    
    label > span {
      /* position: absolute takes the element out of the
         document flow and layout calculations: */
      position: absolute;
      /* aligning the text to the end of the inline axis,
         in English that's equivalent to 'right': */
      text-align: end;
      /* translating the element 95% of its own width,
         the -95% moves it towards the start of the
         inline axis (left in a left-to-right language): */
      transform: translateX(-95%);
      /* setting the width to be equal to the width of
         the <input>: */
      width: var(--inputWidth);
    }
    
    
    /* styling the pseudo-element to automatically display
       a presentational ':' character following the 
       content: */
    label > span::after {
      content: ':';
    }
    
    input {
      width: var(--inputWidth);
    }
    
    
    /* styling the <button> so as not to have it span the full
       width of the flex parent: */
    button {
      padding-block: 0.25em;
      padding-inline: 0.5em;
      width: fit-content;
    }
    <h1>Login</h1>
    
    <form>
      <!-- I've moved the <input> inside the <label>, and wrapped its text-content in a <span> for
           styling purposes: -->
      <label for="email">
        <span>Email</span>
        <input type="email" id="email" name="email" required>
        </label>
      <label for="password">
      <span>Password</span>
      <input type="password" id="password" name="password" required>
      </label>
      <button class='button' id='loginbutton' type="submit">Login</button>
    </form>

    JS Fiddle demo.

    References: