Hộp linh hoạt

The CSS Podcast – 010: Flexbox

Một mẫu thiết kế có thể gây khó khăn trong thiết kế thích ứng là thanh bên nằm cùng dòng với một số nội dung. Khi có không gian khung nhìn, mẫu này hoạt động rất hiệu quả, nhưng khi không gian bị thu hẹp, bố cục cứng nhắc đó có thể gặp vấn đề.

Mô hình bố cục hộp linh hoạt (flexbox) là một mô hình bố cục được thiết kế cho nội dung một chiều. Phương thức này rất hiệu quả trong việc lấy một loạt các mục có kích thước khác nhau và trả về bố cục tốt nhất cho các mục đó.

Đây là mô hình bố cục lý tưởng cho mẫu thanh bên này. Flexbox không chỉ giúp bố trí thanh bên và nội dung cùng dòng, mà còn giúp thanh bên chuyển sang dòng mới khi không còn đủ không gian. Thay vì đặt kích thước cứng nhắc để trình duyệt tuân theo, với flexbox, bạn có thể cung cấp các ranh giới linh hoạt để gợi ý cách nội dung có thể hiển thị.

Bạn có thể làm gì với bố cục flex?

Bố cục Flex có các tính năng sau đây mà bạn có thể khám phá trong hướng dẫn này.

  • Các giá trị này có thể hiển thị dưới dạng hàng hoặc cột.
  • Chúng tuân theo chế độ viết của tài liệu.
  • Theo mặc định, các thành phần này là một dòng nhưng có thể được yêu cầu gói vào nhiều dòng.
  • Bạn có thể sắp xếp lại các mục trong bố cục theo cách trực quan, khác với thứ tự của các mục đó trong DOM.
  • Không gian có thể được phân phối bên trong các mục, nhờ đó các mục này sẽ lớn hơn và nhỏ hơn theo không gian có sẵn trong mục mẹ.
  • Bạn có thể phân bổ không gian xung quanh các mục và các đường linh hoạt trong một bố cục gói, bằng cách sử dụng các thuộc tính Căn chỉnh hộp.
  • Các mục có thể được căn chỉnh trên trục chéo.

Trục chính và trục chéo

Để hiểu rõ flexbox, bạn cần nắm được khái niệm về trục chính và trục chéo. Trục chính là trục do thuộc tính flex-direction đặt. Nếu đó là row, trục chính của bạn sẽ nằm dọc theo hàng, còn nếu đó là column, trục chính của bạn sẽ nằm dọc theo cột.

Ba hộp cạnh nhau có một mũi tên chỉ từ trái sang phải. Mũi tên được gắn nhãn Trục chính

Các mục linh hoạt di chuyển như một nhóm trên trục chính. Hãy nhớ: chúng ta có một loạt các thành phần và đang cố gắng tìm bố cục tốt nhất cho các thành phần đó.

Trục chéo chạy theo hướng khác với trục chính, vì vậy nếu flex-directionrow thì trục chéo sẽ chạy dọc theo cột.

Ba hộp có chiều cao khác nhau, nằm cạnh nhau với một mũi tên chỉ từ trái sang phải. Mũi tên được gắn nhãn Trục chính. Có một mũi tên khác chỉ từ trên xuống dưới. Trục này được gắn nhãn Trục chéo

Bạn có thể thực hiện 2 việc trên trục chéo. Bạn có thể di chuyển các mục riêng lẻ hoặc theo nhóm để các mục đó căn chỉnh với nhau và với vùng chứa flex. Ngoài ra, nếu đã gói các dòng flex, bạn có thể coi các dòng đó là một nhóm để kiểm soát cách phân bổ không gian cho các dòng đó. Bạn sẽ thấy cách hoạt động của tất cả những điều này trong thực tế trong suốt hướng dẫn này, hiện chỉ cần lưu ý rằng trục chính nằm sau flex-direction.

Tạo vùng chứa linh hoạt

Hãy xem cách hoạt động của flexbox bằng cách lấy một nhóm các mục có kích thước khác nhau và sử dụng flexbox để bố trí.

<div class="container" id="container">
  <div>One</div>
  <div>Item two</div>
  <div>The item we will refer to as three</div>
</div>

Để sử dụng flexbox, bạn cần khai báo rằng bạn muốn sử dụng ngữ cảnh định dạng flex chứ không phải khối thông thường và bố cục nội tuyến. Bạn có thể thực hiện việc này bằng cách thay đổi giá trị của thuộc tính display thành flex.

.container {
  display: flex;
}

Như bạn đã tìm hiểu trong hướng dẫn bố cục, tuỳ chọn này sẽ cung cấp cho bạn một hộp cấp khối với các phần tử con linh hoạt của mục. Các mục flex bắt đầu hiển thị một số hành vi flexbox ngay lập tức, sử dụng giá trị ban đầu.

Giá trị ban đầu có nghĩa là:

  • Các mục hiển thị dưới dạng một hàng.
  • Chúng không xuống dòng tự động.
  • Các thành phần này không phát triển để lấp đầy vùng chứa.
  • Các tham số này xếp hàng ở đầu vùng chứa.

Kiểm soát hướng của mục

Mặc dù bạn chưa thêm thuộc tính flex-direction, nhưng các mục sẽ hiển thị dưới dạng một hàng vì giá trị ban đầu của flex-directionrow. Nếu muốn một hàng, bạn không cần thêm thuộc tính này. Để thay đổi hướng, hãy thêm thuộc tính và một trong bốn giá trị sau:

  • row: các mục được sắp xếp thành một hàng.
  • row-reverse: các mục được bố trí dưới dạng hàng từ cuối vùng chứa linh hoạt.
  • column: các mục được sắp xếp dưới dạng cột.
  • column-reverse : các mục được sắp xếp dưới dạng một cột từ cuối vùng chứa flex.

Bạn có thể thử tất cả các giá trị bằng cách sử dụng nhóm mục trong bản minh hoạ bên dưới.

Đảo ngược luồng mục và khả năng hỗ trợ tiếp cận

Bạn nên thận trọng khi sử dụng bất kỳ thuộc tính nào sắp xếp lại cách hiển thị hình ảnh khác với cách sắp xếp trong tài liệu HTML, vì điều này có thể ảnh hưởng tiêu cực đến khả năng hỗ trợ tiếp cận. Giá trị row-reversecolumn-reverse là ví dụ điển hình cho điều này. Việc sắp xếp lại chỉ xảy ra đối với thứ tự hình ảnh chứ không phải thứ tự logic. Bạn cần hiểu điều này vì thứ tự logic là thứ tự mà trình đọc màn hình sẽ đọc nội dung và bất kỳ ai điều hướng bằng bàn phím sẽ tuân theo.

Trong video sau, bạn có thể thấy cách bố cục hàng đảo ngược, việc tab giữa các đường liên kết bị ngắt kết nối khi thao tác điều hướng bằng bàn phím đi theo DOM chứ không phải màn hình trực quan.

Mọi thứ có thể thay đổi thứ tự của các mục trong flexbox hoặc lưới đều có thể gây ra vấn đề này. Do đó, mọi lần sắp xếp lại đều phải bao gồm việc kiểm thử kỹ lưỡng để đảm bảo rằng việc này sẽ không khiến một số người khó sử dụng trang web của bạn.

Để biết thêm thông tin, hãy xem:

Chế độ và hướng viết

Theo mặc định, các mục Flex được bố trí dưới dạng một hàng. Hàng chạy theo hướng các câu chảy trong chế độ viết và hướng tập lệnh. Điều này có nghĩa là nếu bạn đang làm việc bằng tiếng Ả Rập, có hướng chữ viết từ phải sang trái (rtl), thì các mục sẽ xếp hàng ở bên phải. Thứ tự phím tab cũng sẽ bắt đầu ở bên phải vì đây là cách đọc câu trong tiếng Ả Rập.

Nếu bạn đang làm việc với chế độ viết dọc, như một số kiểu chữ tiếng Nhật, thì một hàng sẽ chạy theo chiều dọc, từ trên xuống dưới. Hãy thử thay đổi flex-direction trong bản minh hoạ này (đang sử dụng chế độ viết dọc).

Do đó, cách các mục flex hoạt động theo mặc định được liên kết với chế độ ghi của tài liệu. Hầu hết các hướng dẫn đều được viết bằng tiếng Anh hoặc một chế độ viết ngang khác, từ trái sang phải. Điều này giúp bạn dễ dàng giả định rằng các mục flex sẽ xếp hàng ở bên trái và chạy theo chiều ngang.

Với trục chính và trục chéo cùng với chế độ viết cần xem xét, có thể việc chúng ta nói về bắt đầukết thúc thay vì trên cùng, dưới cùng, bên trái và bên phải trong hộp linh hoạt có thể dễ hiểu hơn. Mỗi trục có một điểm bắt đầu và một điểm kết thúc. Điểm bắt đầu của trục chính được gọi là main-start. Vì vậy, các mục linh hoạt của chúng ta sẽ được sắp xếp từ đầu đến cuối. Điểm cuối của trục đó là đầu trục chính. Điểm bắt đầu của trục chéo là cross-start và điểm kết thúc là cross-end.

Sơ đồ có gắn nhãn của các thuật ngữ ở trên

Gói các mục flex

Giá trị ban đầu của thuộc tính flex-wrapnowrap. Điều này có nghĩa là nếu không có đủ không gian trong vùng chứa, các mục sẽ tràn.

Một vùng chứa flex có 9 mục bên trong, các mục đã thu nhỏ để một từ nằm trên một dòng nhưng không đủ chỗ để hiển thị các mục đó cạnh nhau, vì vậy các mục flex đã mở rộng ra bên ngoài hộp của vùng chứa.
Khi họ đạt đến kích thước nội dung tối thiểu, các mục linh hoạt sẽ bắt đầu tràn vùng chứa

Các mục hiển thị bằng giá trị ban đầu sẽ thu nhỏ nhất có thể, xuống kích thước min-content trước khi bị tràn.

Để các mục được gói, hãy thêm flex-wrap: wrap vào vùng chứa flex.

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

Khi một vùng chứa flex gói, vùng chứa đó sẽ tạo nhiều dòng flex. Khi phân bổ không gian, mỗi dòng đóng vai trò như một vùng chứa linh hoạt mới. Do đó, nếu bạn đang gói các hàng, thì không thể căn chỉnh nội dung trong hàng 2 với nội dung ở phía trên trong hàng 1. Đây là ý nghĩa của việc flexbox là một chiều. Bạn có thể kiểm soát căn chỉnh theo một trục, một hàng hoặc một cột, không phải cả hai cùng lúc như chúng ta có thể làm trong lưới.

Viết tắt Flex-flow

Bạn có thể đặt thuộc tính flex-directionflex-wrap bằng cách sử dụng viết tắt flex-flow. Ví dụ: để đặt flex-direction thành column và cho phép các mục gói:

.container {
  display: flex;
  flex-flow: column wrap;
}

Kiểm soát không gian bên trong các mục flex

Giả sử vùng chứa của chúng ta có nhiều không gian hơn cần thiết để hiển thị các mục, các mục sẽ xếp hàng ở đầu và không tăng lên để lấp đầy không gian. Các thành phần này sẽ ngừng phát triển ở kích thước nội dung tối đa. Điều này là do giá trị ban đầu của thuộc tính flex- là:

  • flex-grow: 0: các mục không phát triển.
  • flex-shrink: 1: các mục có thể thu nhỏ hơn flex-basis.
  • flex-basis: auto: các mục có kích thước cơ sở là auto.

Giá trị này có thể được biểu thị bằng giá trị từ khoá là flex: initial. Thuộc tính viết tắt flex hoặc viết tắt của flex-grow, flex-shrinkflex-basis được áp dụng cho phần tử con của vùng chứa linh hoạt.

Để các mục phát triển, đồng thời cho phép các mục lớn có nhiều không gian hơn các mục nhỏ, hãy sử dụng flex:auto. Bạn có thể thử cách này bằng bản minh hoạ ở trên. Thao tác này sẽ đặt các thuộc tính thành:

  • flex-grow: 1: các mục có thể lớn hơn flex-basis.
  • flex-shrink: 1: các mục có thể thu nhỏ nhỏ hơn flex-basis.
  • flex-basis: auto: các mục có kích thước cơ sở là auto.

Việc sử dụng flex: auto sẽ khiến các mục có kích thước khác nhau, vì không gian được chia sẻ giữa các mục được chia sẻ sau khi mỗi mục được bố trí dưới dạng kích thước nội dung tối đa. Vì vậy, một mục lớn sẽ có nhiều không gian hơn. Để buộc tất cả các mục có kích thước nhất quán và bỏ qua kích thước của nội dung thay đổi từ flex:auto thành flex: 1 trong bản minh hoạ.

Thao tác này sẽ giải nén thành:

  • flex-grow: 1: các mục có thể lớn hơn flex-basis.
  • flex-shrink: 1: các mục có thể thu nhỏ nhỏ hơn flex-basis.
  • flex-basis: 0: các mục có kích thước cơ sở là 0.

Việc sử dụng flex: 1 cho biết tất cả các mục đều có kích thước bằng 0, vì vậy, tất cả không gian trong vùng chứa flex đều có thể được phân phối. Vì tất cả các mục đều có hệ số flex-grow1, nên tất cả các mục đều tăng lên như nhau và không gian được chia sẻ như nhau.

Cho phép mặt hàng tăng trưởng ở các tỷ lệ khác nhau

Bạn không bắt buộc phải đặt hệ số flex-grow của tất cả các mục là 1. Bạn có thể cung cấp cho các phần tử flex các hệ số flex-grow khác nhau. Trong bản minh hoạ bên dưới, mục đầu tiên có flex: 1, mục thứ hai có flex: 2 và mục thứ ba có flex: 3. Khi các mục này tăng từ 0, không gian có sẵn trong vùng chứa flex được chia thành 6 phần. Một phần được cấp cho mục đầu tiên, hai phần cho mục thứ hai, ba phần cho mục thứ ba.

Bạn có thể thực hiện tương tự từ flex-basis của auto, mặc dù bạn sẽ cần chỉ định ba giá trị. Giá trị đầu tiên là flex-grow, giá trị thứ hai là flex-shrink và giá trị thứ ba là flex-basis.

.item1 {
  flex: 1 1 auto;
}

.item2 {
  flex: 2 1 auto;
}

Đây là trường hợp sử dụng ít phổ biến hơn vì lý do sử dụng flex-basis của auto là để cho phép trình duyệt tìm ra cách phân bổ không gian. Tuy nhiên, nếu bạn muốn một mục tăng thêm một chút so với quyết định của thuật toán, thì điều này có thể hữu ích.

Sắp xếp lại các mục linh hoạt

Bạn có thể sắp xếp lại các mục trong vùng chứa flex bằng thuộc tính order. Thuộc tính này cho phép sắp xếp các mục trong nhóm thứ tự. Các mục được sắp xếp theo hướng do flex-direction chỉ định, giá trị thấp nhất trước. Nếu có nhiều mục có cùng một giá trị, thì mục đó sẽ xuất hiện cùng với các mục khác có giá trị đó.

Ví dụ dưới đây minh hoạ thứ tự này.

Kiểm tra mức độ hiểu biết

Kiểm tra kiến thức của bạn về flexbox

Mặc định flex-direction

row
Theo mặc định, flexbox sẽ vừa với các mục trong một hàng, xếp các mục đó lại với nhau ở đầu. Khi bạn bật tính năng gói, tính năng này sẽ tiếp tục tạo các hàng để các phần tử con luân chuyển trong đó.
column
Đặt hướng linh hoạt thành cột là một cách hay để xếp chồng các phần tử, nhưng đây không phải là giá trị mặc định.

Theo mặc định, một vùng chứa linh hoạt sẽ bao bọc các phần tử con.

đúng
Bạn phải bật tính năng gói.
false
Sử dụng flex-wrap: wrap với display: flex để gói các phần tử con

Một mục con flex có vẻ bị vỡ, thuộc tính flex nào giúp giảm thiểu điều này?

flex-grow
Thuộc tính này mô tả liệu các phần tử có thể phát triển vượt quá kích thước cơ sở hay không, chứ không phải cách hoạt động của các phần tử đó trong cơ sở.
flex-shrink
Có, thuộc tính này mô tả cách xử lý kích thước nếu chiều rộng giảm xuống dưới cơ sở.
flex-basis
Đây là điểm xuất phát của việc định cỡ, nhưng không phải là cách xử lý các trường hợp định cỡ mà chiều rộng thấp hơn cơ sở, chẳng hạn như trong trường hợp bị vỡ.

Tổng quan về cách căn chỉnh Flexbox

Flexbox có một tập hợp các thuộc tính để căn chỉnh các mục và phân phối không gian giữa các mục. Các thuộc tính này rất hữu ích nên đã được chuyển vào quy cách riêng, bạn cũng sẽ gặp các thuộc tính này trong Bố cục lưới. Tại đây, bạn có thể tìm hiểu cách hoạt động của các thuộc tính này khi sử dụng flexbox.

Bạn có thể đặt tập hợp thuộc tính vào hai nhóm. Các thuộc tính để phân phối không gian và các thuộc tính để căn chỉnh. Các thuộc tính phân phối không gian là:

  • justify-content: phân phối không gian trên trục chính.
  • align-content: phân phối không gian trên trục chéo.
  • place-content: viết tắt để đặt cả hai thuộc tính ở trên.

Các thuộc tính dùng để căn chỉnh trong hộp linh hoạt:

  • align-self: căn chỉnh một mục trên trục chéo.
  • align-items: căn chỉnh tất cả các mục dưới dạng một nhóm trên trục chéo.

Nếu bạn đang làm việc trên trục chính, thì các thuộc tính sẽ bắt đầu bằng justify-. Trên trục chéo, chúng bắt đầu bằng align-.

Phân bổ không gian trên trục chính

Với HTML được sử dụng trước đó, các mục flex được bố trí dưới dạng một hàng, có không gian trên trục chính. Các mục không đủ lớn để lấp đầy hoàn toàn vùng chứa linh hoạt. Các mục được căn chỉnh ở đầu vùng chứa flex vì giá trị ban đầu của justify-contentflex-start. Các mục được căn chỉnh ở đầu và mọi khoảng trắng thừa đều ở cuối.

Thêm thuộc tính justify-content vào vùng chứa flex, gán giá trị flex-end cho thuộc tính này, các mục sẽ xếp hàng ở cuối vùng chứa và không gian trống được đặt ở đầu.

.container {
  display: flex;
  justify-content: flex-end;
}

Bạn cũng có thể phân phối khoảng trống giữa các mục bằng justify-content: space-between.

Hãy thử một số giá trị trong bản minh hoạ và xem MDN để biết toàn bộ tập hợp giá trị có thể có.

Thông qua flex-direction: column

Nếu bạn đã thay đổi flex-direction thành column, thì justify-content sẽ hoạt động trên cột. Để có không gian trống trong vùng chứa khi hoạt động dưới dạng cột, bạn cần cung cấp cho vùng chứa một height hoặc block-size. Nếu không, bạn sẽ không có không gian trống để phân phối.

Hãy thử các giá trị khác nhau, lần này với bố cục cột flexbox.

Phân phối không gian giữa các đường linh hoạt

Với vùng chứa flex được gói, bạn có thể có không gian để phân phối trên trục chéo. Trong trường hợp này, bạn có thể sử dụng thuộc tính align-content với các giá trị giống như justify-content. Không giống như justify-content (được căn chỉnh các mục với flex-start theo mặc định), giá trị ban đầu của align-contentstretch. Thêm thuộc tính align-content vào vùng chứa flex để thay đổi hành vi mặc định đó.

.container {
  align-content: center;
}

Hãy thử cách này trong bản minh hoạ. Ví dụ này đã gói các dòng của các mục flex và vùng chứa có block-size để chúng ta có một số không gian trống.

Ký hiệu viết tắt place-content

Để đặt cả justify-contentalign-content, bạn có thể sử dụng place-content với một hoặc hai giá trị. Một giá trị duy nhất sẽ được sử dụng cho cả hai trục, nếu bạn chỉ định cả giá trị đầu tiên được dùng cho align-content và giá trị thứ hai cho justify-content.

.container {
  place-content: space-between;
  /* sets both to space-between */
}

.container {
  place-content: center flex-end;
  /* wrapped lines on the cross axis are centered,
  on the main axis items are aligned to the end of the flex container */
}

Căn chỉnh các mục trên trục chéo

Trên trục chéo, bạn cũng có thể căn chỉnh các mục trong dòng flex bằng align-itemsalign-self. Không gian có sẵn cho việc căn chỉnh này sẽ phụ thuộc vào chiều cao của vùng chứa linh hoạt hoặc đường linh hoạt trong trường hợp một tập hợp các mục được gói.

Giá trị ban đầu của align-selfstretch, đó là lý do các mục flex trong một hàng sẽ kéo giãn theo chiều cao của mục cao nhất theo mặc định. Để thay đổi giá trị này, hãy thêm thuộc tính align-self vào bất kỳ mục linh hoạt nào của bạn.

.container {
  display: flex;
}

.item1 {
  align-self: flex-start;
}

Sử dụng bất kỳ giá trị nào sau đây để căn chỉnh mục:

  • flex-start
  • flex-end
  • center
  • stretch
  • baseline

Xem danh sách đầy đủ các giá trị trên MDN.

Bản minh hoạ tiếp theo có một dòng các mục flex với flex-direction: row. Mục cuối cùng xác định chiều cao của vùng chứa flex. Mục đầu tiên có thuộc tính align-self với giá trị là flex-start. Hãy thử thay đổi giá trị trên thuộc tính đó để xem giá trị đó di chuyển như thế nào trong không gian của nó trên trục chéo.

Thuộc tính align-self được áp dụng cho từng mục. Bạn có thể áp dụng thuộc tính align-items cho vùng chứa flex để đặt tất cả các thuộc tính align-self riêng lẻ dưới dạng một nhóm.

.container {
  display: flex;
  align-items: flex-start;
}

Trong bản minh hoạ tiếp theo, hãy thử thay đổi giá trị của align-items để căn chỉnh tất cả các mục trên trục chéo dưới dạng một nhóm.

Tại sao không có justify-self trong flexbox?

Các mục linh hoạt hoạt động như một nhóm trên trục chính. Vì vậy, không có khái niệm về việc tách một mục riêng lẻ ra khỏi nhóm đó.

Trong bố cục lưới, các thuộc tính justify-selfjustify-items hoạt động trên trục nội tuyến để căn chỉnh các mục trên trục đó trong vùng lưới của chúng. Do cách bố cục flex coi các mục là một nhóm, nên các thuộc tính này không được triển khai trong ngữ cảnh flex.

Điều đáng biết là flexbox hoạt động rất tốt với lề tự động. Nếu cần tách một mục khỏi một nhóm hoặc tách nhóm thành hai nhóm, bạn có thể áp dụng lề để thực hiện việc này. Trong ví dụ bên dưới, mục cuối cùng có lề trái là auto. Lề tự động sẽ hấp thụ tất cả không gian theo hướng áp dụng. Điều này có nghĩa là nó đẩy mục sang phải, do đó tách các nhóm.

Cách căn giữa một mục theo chiều dọc và chiều ngang

Bạn có thể dùng thuộc tính căn chỉnh để căn giữa một mục bên trong một hộp khác. Thuộc tính justify-content căn chỉnh mục trên trục chính, đó là hàng. Thuộc tính align-items trên trục chéo.

.container {
  width: 400px;
  height: 300px;
  display: flex;
  justify-content: center;
  align-items: center;
}

Kiểm tra kiến thức

Kiểm tra kiến thức của bạn về flexbox

.container {
  display: flex;
  direction: ltr;
}

Để căn chỉnh theo chiều dọc với flexbox, hãy sử dụng

căn chỉnh từ khoá
Rất tốt
căn chỉnh từ khoá
Rất tiếc
.container {
  display: flex;
  direction: ltr;
}

Để căn chỉnh theo chiều ngang với hộp linh hoạt, hãy sử dụng

căn chỉnh từ khoá
Rất tiếc
căn chỉnh từ khoá
Rất tốt
.container {
  display: flex;
  direction: ltr;
}

Theo mặc định, các mục linh hoạt sẽ được căn chỉnh theo stretch. Nếu muốn sử dụng kích thước nội dung cho các mục con, bạn sẽ sử dụng kiểu nào sau đây?

justify-content: flex-start
Thuộc tính căn chỉnh là để căn chỉnh theo chiều ngang, chứ không phải theo chiều dọc.
align-content: start
content căn chỉnh các dòng flex, chứ không phải căn chỉnh mục con.
height: auto
Việc này sẽ không có hiệu lực.
align-items: flex-start
Có, chúng ta muốn căn chỉnh theo chiều dọc với 'trên cùng' hoặc bắt đầu, việc này sẽ loại bỏ giá trị kéo giãn mặc định và thay vào đó sử dụng chiều cao của nội dung.

Tài nguyên